open-agents-ai 0.35.2 → 0.36.0

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
@@ -2906,6 +2906,7 @@ var init_explore_tools = __esm({
2906
2906
  code_sandbox: "Run code in an isolated sandbox",
2907
2907
  structured_read: "Read structured data files (CSV, JSON)",
2908
2908
  sub_agent: "Delegate a subtask to a new agent instance",
2909
+ browser_action: "Automate headless Chrome: navigate, click, type, screenshot, read DOM",
2909
2910
  background_run: "Run a shell command in the background",
2910
2911
  task_status: "Check status of background tasks",
2911
2912
  task_output: "Read output from a background task",
@@ -8650,6 +8651,259 @@ Note: Advanced Python pipeline not available \u2014 install pytesseract, opencv-
8650
8651
  }
8651
8652
  });
8652
8653
 
8654
+ // packages/execution/dist/tools/browser-action.js
8655
+ import { execSync as execSync15, spawn as spawn7 } from "node:child_process";
8656
+ import { existsSync as existsSync18, readFileSync as readFileSync13 } from "node:fs";
8657
+ import { join as join21, dirname as dirname8 } from "node:path";
8658
+ import { fileURLToPath as fileURLToPath5 } from "node:url";
8659
+ async function probeService() {
8660
+ try {
8661
+ const controller = new AbortController();
8662
+ const timeout = setTimeout(() => controller.abort(), 3e3);
8663
+ const res = await fetch(`${BASE_URL}/health`, { signal: controller.signal });
8664
+ clearTimeout(timeout);
8665
+ return res.ok;
8666
+ } catch {
8667
+ return false;
8668
+ }
8669
+ }
8670
+ function findPython2() {
8671
+ for (const cmd of ["python3", "python"]) {
8672
+ try {
8673
+ const ver = execSync15(`${cmd} --version 2>&1`, { stdio: "pipe", timeout: 5e3 }).toString().trim();
8674
+ if (ver.includes("Python 3"))
8675
+ return cmd;
8676
+ } catch {
8677
+ }
8678
+ }
8679
+ return null;
8680
+ }
8681
+ async function launchService() {
8682
+ if (await probeService())
8683
+ return null;
8684
+ const python = findPython2();
8685
+ if (!python)
8686
+ return "Python 3 not found. Install Python 3.9+ to use browser automation.";
8687
+ if (!existsSync18(SCRAPE_SCRIPT)) {
8688
+ return `Scrape service script not found at ${SCRAPE_SCRIPT}`;
8689
+ }
8690
+ serviceProcess = spawn7(python, [SCRAPE_SCRIPT], {
8691
+ stdio: "ignore",
8692
+ detached: true,
8693
+ env: {
8694
+ ...process.env,
8695
+ SCRAPE_PORT: String(DEFAULT_PORT),
8696
+ SCRAPE_HEADLESS_DEFAULT: "1",
8697
+ SCRAPE_REQUIRE_AUTH: "0"
8698
+ }
8699
+ });
8700
+ serviceProcess.unref();
8701
+ for (let i = 0; i < 120; i++) {
8702
+ await new Promise((r) => setTimeout(r, 500));
8703
+ if (await probeService())
8704
+ return null;
8705
+ }
8706
+ return "Scrape service failed to start within 60s. Check Chrome/Chromium installation.";
8707
+ }
8708
+ async function ensureSession() {
8709
+ if (activeSessionId) {
8710
+ try {
8711
+ const res2 = await fetch(`${BASE_URL}/health`);
8712
+ if (res2.ok)
8713
+ return { sessionId: activeSessionId };
8714
+ } catch {
8715
+ }
8716
+ activeSessionId = null;
8717
+ }
8718
+ const res = await fetch(`${BASE_URL}/session/start`, {
8719
+ method: "POST",
8720
+ headers: { "Content-Type": "application/json" },
8721
+ body: JSON.stringify({ headless: true })
8722
+ });
8723
+ const data = await res.json();
8724
+ if (!data.ok)
8725
+ return { error: String(data.message ?? "Failed to start browser session"), sessionId: "" };
8726
+ activeSessionId = data.session_id;
8727
+ return { sessionId: activeSessionId };
8728
+ }
8729
+ async function apiCall(endpoint, method = "POST", body) {
8730
+ const options = {
8731
+ method,
8732
+ headers: { "Content-Type": "application/json" }
8733
+ };
8734
+ if (body && method === "POST") {
8735
+ options.body = JSON.stringify({ sid: activeSessionId, ...body });
8736
+ }
8737
+ let url = `${BASE_URL}${endpoint}`;
8738
+ if (method === "GET" && activeSessionId) {
8739
+ url += `?sid=${encodeURIComponent(activeSessionId)}`;
8740
+ }
8741
+ const res = await fetch(url, options);
8742
+ return await res.json();
8743
+ }
8744
+ var __dirname2, SCRAPE_SCRIPT, DEFAULT_PORT, BASE_URL, serviceProcess, activeSessionId, BrowserActionTool;
8745
+ var init_browser_action = __esm({
8746
+ "packages/execution/dist/tools/browser-action.js"() {
8747
+ "use strict";
8748
+ __dirname2 = dirname8(fileURLToPath5(import.meta.url));
8749
+ SCRAPE_SCRIPT = join21(__dirname2, "..", "..", "scripts", "web_scrape.py");
8750
+ DEFAULT_PORT = 8130;
8751
+ BASE_URL = `http://localhost:${DEFAULT_PORT}`;
8752
+ serviceProcess = null;
8753
+ activeSessionId = null;
8754
+ BrowserActionTool = class {
8755
+ name = "browser_action";
8756
+ description = "Automate a headless Chrome browser: navigate to URLs, click elements, type text, take screenshots, read DOM content, scroll, and navigate history. Uses a local Selenium-based service that auto-starts on first use. Better than xdotool for web automation \u2014 works headlessly without a display server. Actions: navigate, click, click_xy, type, screenshot, dom, scroll, back, forward, close.";
8757
+ parameters = {
8758
+ type: "object",
8759
+ properties: {
8760
+ action: {
8761
+ type: "string",
8762
+ enum: ["navigate", "click", "click_xy", "type", "screenshot", "dom", "scroll", "scroll_up", "scroll_down", "back", "forward", "close"],
8763
+ description: "Browser action to perform"
8764
+ },
8765
+ url: {
8766
+ type: "string",
8767
+ description: "URL to navigate to (for 'navigate' action)"
8768
+ },
8769
+ selector: {
8770
+ type: "string",
8771
+ description: "CSS selector of element to interact with (for 'click' and 'type' actions)"
8772
+ },
8773
+ text: {
8774
+ type: "string",
8775
+ description: "Text to type into the selected element (for 'type' action)"
8776
+ },
8777
+ x: {
8778
+ type: "number",
8779
+ description: "X coordinate for click_xy action (viewport pixels)"
8780
+ },
8781
+ y: {
8782
+ type: "number",
8783
+ description: "Y coordinate for click_xy action (viewport pixels)"
8784
+ },
8785
+ amount: {
8786
+ type: "number",
8787
+ description: "Scroll amount in pixels (for 'scroll' action, default 600)"
8788
+ }
8789
+ },
8790
+ required: ["action"]
8791
+ };
8792
+ async execute(args) {
8793
+ const start = Date.now();
8794
+ const action = args.action;
8795
+ const launchErr = await launchService();
8796
+ if (launchErr) {
8797
+ return { success: false, output: "", error: launchErr, durationMs: Date.now() - start };
8798
+ }
8799
+ if (action === "close") {
8800
+ if (activeSessionId) {
8801
+ try {
8802
+ await apiCall("/session/close");
8803
+ } catch {
8804
+ }
8805
+ activeSessionId = null;
8806
+ }
8807
+ return { success: true, output: "Browser session closed.", durationMs: Date.now() - start };
8808
+ }
8809
+ const session = await ensureSession();
8810
+ if (session.error) {
8811
+ return { success: false, output: "", error: session.error, durationMs: Date.now() - start };
8812
+ }
8813
+ try {
8814
+ let result;
8815
+ switch (action) {
8816
+ case "navigate":
8817
+ if (!args.url)
8818
+ return { success: false, output: "", error: "url is required for navigate action", durationMs: Date.now() - start };
8819
+ result = await apiCall("/navigate", "POST", { url: args.url });
8820
+ return {
8821
+ success: !!result.ok,
8822
+ output: result.ok ? `Navigated to ${args.url}` : String(result.message ?? "Navigation failed"),
8823
+ durationMs: Date.now() - start
8824
+ };
8825
+ case "click":
8826
+ if (!args.selector)
8827
+ return { success: false, output: "", error: "selector is required for click action", durationMs: Date.now() - start };
8828
+ result = await apiCall("/click", "POST", { selector: args.selector });
8829
+ return {
8830
+ success: !!result.ok,
8831
+ output: result.ok ? `Clicked element: ${args.selector}` : String(result.message ?? "Click failed"),
8832
+ durationMs: Date.now() - start
8833
+ };
8834
+ case "click_xy":
8835
+ if (args.x == null || args.y == null)
8836
+ return { success: false, output: "", error: "x and y are required for click_xy action", durationMs: Date.now() - start };
8837
+ result = await apiCall("/click_xy", "POST", { x: args.x, y: args.y });
8838
+ return {
8839
+ success: !!result.ok,
8840
+ output: result.ok ? `Clicked at (${args.x}, ${args.y})` : String(result.message ?? "Click failed"),
8841
+ durationMs: Date.now() - start
8842
+ };
8843
+ case "type":
8844
+ if (!args.selector || !args.text)
8845
+ return { success: false, output: "", error: "selector and text are required for type action", durationMs: Date.now() - start };
8846
+ result = await apiCall("/type", "POST", { selector: args.selector, text: args.text });
8847
+ return {
8848
+ success: !!result.ok,
8849
+ output: result.ok ? `Typed "${args.text.slice(0, 50)}" into ${args.selector}` : String(result.message ?? "Type failed"),
8850
+ durationMs: Date.now() - start
8851
+ };
8852
+ case "screenshot": {
8853
+ result = await apiCall("/screenshot", "GET");
8854
+ const b64 = result.b64;
8855
+ const width = result.width;
8856
+ const height = result.height;
8857
+ if (b64) {
8858
+ return {
8859
+ success: true,
8860
+ output: `Screenshot captured (${width}x${height}). Base64 length: ${b64.length} chars.
8861
+ [IMAGE_BASE64:image/png:${b64.slice(0, 200)}...]`,
8862
+ durationMs: Date.now() - start
8863
+ };
8864
+ }
8865
+ return { success: false, output: "", error: "Screenshot failed", durationMs: Date.now() - start };
8866
+ }
8867
+ case "dom": {
8868
+ result = await apiCall("/dom", "GET");
8869
+ const dom = result.dom;
8870
+ if (dom) {
8871
+ const truncated = dom.length > 5e4 ? dom.slice(0, 5e4) + "\n... (truncated)" : dom;
8872
+ return { success: true, output: truncated, durationMs: Date.now() - start };
8873
+ }
8874
+ return { success: false, output: "", error: "DOM capture failed", durationMs: Date.now() - start };
8875
+ }
8876
+ case "scroll":
8877
+ result = await apiCall("/scroll", "POST", { amount: args.amount ?? 600 });
8878
+ return { success: !!result.ok, output: `Scrolled ${args.amount ?? 600}px`, durationMs: Date.now() - start };
8879
+ case "scroll_up":
8880
+ result = await apiCall("/scroll/up", "POST");
8881
+ return { success: !!result.ok, output: "Scrolled up", durationMs: Date.now() - start };
8882
+ case "scroll_down":
8883
+ result = await apiCall("/scroll/down", "POST");
8884
+ return { success: !!result.ok, output: "Scrolled down", durationMs: Date.now() - start };
8885
+ case "back":
8886
+ result = await apiCall("/history/back", "POST");
8887
+ return { success: !!result.ok, output: "Navigated back", durationMs: Date.now() - start };
8888
+ case "forward":
8889
+ result = await apiCall("/history/forward", "POST");
8890
+ return { success: !!result.ok, output: "Navigated forward", durationMs: Date.now() - start };
8891
+ default:
8892
+ return { success: false, output: "", error: `Unknown action: ${action}. Available: navigate, click, click_xy, type, screenshot, dom, scroll, scroll_up, scroll_down, back, forward, close`, durationMs: Date.now() - start };
8893
+ }
8894
+ } catch (err) {
8895
+ return {
8896
+ success: false,
8897
+ output: "",
8898
+ error: `Browser action failed: ${err instanceof Error ? err.message : String(err)}`,
8899
+ durationMs: Date.now() - start
8900
+ };
8901
+ }
8902
+ }
8903
+ };
8904
+ }
8905
+ });
8906
+
8653
8907
  // packages/execution/dist/shellRunner.js
8654
8908
  var init_shellRunner = __esm({
8655
8909
  "packages/execution/dist/shellRunner.js"() {
@@ -8765,6 +9019,7 @@ var init_dist2 = __esm({
8765
9019
  init_ocr_pdf();
8766
9020
  init_pdf_to_text();
8767
9021
  init_ocr_image_advanced();
9022
+ init_browser_action();
8768
9023
  init_system_deps();
8769
9024
  init_shellRunner();
8770
9025
  init_gitWorktree();
@@ -9897,7 +10152,7 @@ var init_code_retriever = __esm({
9897
10152
  import { execFile as execFile5 } from "node:child_process";
9898
10153
  import { promisify as promisify4 } from "node:util";
9899
10154
  import { readFile as readFile9, readdir as readdir3, stat as stat3 } from "node:fs/promises";
9900
- import { join as join21, extname as extname7 } from "node:path";
10155
+ import { join as join22, extname as extname7 } from "node:path";
9901
10156
  async function searchByPath(pathPattern, options) {
9902
10157
  const allFiles = await collectFiles(options.rootDir, options.includeGlobs ?? DEFAULT_INCLUDE_GLOBS, options.excludeGlobs ?? DEFAULT_EXCLUDE_GLOBS);
9903
10158
  const pattern = options.caseInsensitive ? pathPattern.toLowerCase() : pathPattern;
@@ -10039,7 +10294,7 @@ async function walkForFiles(rootDir, dir, excludeGlobs, results) {
10039
10294
  continue;
10040
10295
  if (excludeGlobs.some((g) => entry.name === g || matchesGlob(entry.name, g)))
10041
10296
  continue;
10042
- const absPath = join21(dir, entry.name);
10297
+ const absPath = join22(dir, entry.name);
10043
10298
  if (entry.isDirectory()) {
10044
10299
  await walkForFiles(rootDir, absPath, excludeGlobs, results);
10045
10300
  } else if (entry.isFile()) {
@@ -10346,7 +10601,7 @@ var init_graphExpand = __esm({
10346
10601
 
10347
10602
  // packages/retrieval/dist/snippetPacker.js
10348
10603
  import { readFile as readFile10 } from "node:fs/promises";
10349
- import { join as join22 } from "node:path";
10604
+ import { join as join23 } from "node:path";
10350
10605
  async function packSnippets(requests, opts = {}) {
10351
10606
  const maxTokens = opts.maxTokens ?? DEFAULT_MAX_TOKENS;
10352
10607
  const contextLines = opts.contextLines ?? DEFAULT_CONTEXT_LINES;
@@ -10372,7 +10627,7 @@ async function packSnippets(requests, opts = {}) {
10372
10627
  return { packed, dropped, totalTokens };
10373
10628
  }
10374
10629
  async function extractSnippet(req, repoRoot, contextLines = DEFAULT_CONTEXT_LINES) {
10375
- const absPath = req.filePath.startsWith("/") ? req.filePath : join22(repoRoot, req.filePath);
10630
+ const absPath = req.filePath.startsWith("/") ? req.filePath : join23(repoRoot, req.filePath);
10376
10631
  let content;
10377
10632
  try {
10378
10633
  content = await readFile10(absPath, "utf-8");
@@ -12298,7 +12553,8 @@ Integrate this guidance into your current approach. Continue working on the task
12298
12553
  "task_output",
12299
12554
  "image_read",
12300
12555
  "screenshot",
12301
- "ocr"
12556
+ "ocr",
12557
+ "browser_action"
12302
12558
  ]);
12303
12559
  const executeSingle = async (tc) => {
12304
12560
  if (this.aborted)
@@ -12789,8 +13045,8 @@ ${marker}` : marker);
12789
13045
  return;
12790
13046
  try {
12791
13047
  const { mkdirSync: mkdirSync13, writeFileSync: writeFileSync12 } = __require("node:fs");
12792
- const { join: join37 } = __require("node:path");
12793
- const sessionDir = join37(this._workingDirectory, ".oa", "session", this._sessionId);
13048
+ const { join: join38 } = __require("node:path");
13049
+ const sessionDir = join38(this._workingDirectory, ".oa", "session", this._sessionId);
12794
13050
  mkdirSync13(sessionDir, { recursive: true });
12795
13051
  const checkpoint = {
12796
13052
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
@@ -12803,7 +13059,7 @@ ${marker}` : marker);
12803
13059
  memexEntryCount: this._memexArchive.size,
12804
13060
  fileRegistrySize: this._fileRegistry.size
12805
13061
  };
12806
- writeFileSync12(join37(sessionDir, "checkpoint.json"), JSON.stringify(checkpoint, null, 2));
13062
+ writeFileSync12(join38(sessionDir, "checkpoint.json"), JSON.stringify(checkpoint, null, 2));
12807
13063
  } catch {
12808
13064
  }
12809
13065
  }
@@ -13759,6 +14015,7 @@ ${transcript}`
13759
14015
  temperature: request.temperature,
13760
14016
  max_tokens: request.maxTokens,
13761
14017
  stream: true,
14018
+ stream_options: { include_usage: true },
13762
14019
  think: true
13763
14020
  };
13764
14021
  const resp = await fetch(`${this.baseUrl}/v1/chat/completions`, {
@@ -14340,11 +14597,11 @@ var init_dist5 = __esm({
14340
14597
  });
14341
14598
 
14342
14599
  // packages/cli/dist/tui/listen.js
14343
- import { spawn as spawn7, execSync as execSync15 } from "node:child_process";
14344
- import { existsSync as existsSync18, mkdirSync as mkdirSync5, writeFileSync as writeFileSync5, readdirSync as readdirSync6 } from "node:fs";
14345
- import { join as join23, dirname as dirname8 } from "node:path";
14600
+ import { spawn as spawn8, execSync as execSync16 } from "node:child_process";
14601
+ import { existsSync as existsSync19, mkdirSync as mkdirSync5, writeFileSync as writeFileSync5, readdirSync as readdirSync6 } from "node:fs";
14602
+ import { join as join24, dirname as dirname9 } from "node:path";
14346
14603
  import { homedir as homedir8 } from "node:os";
14347
- import { fileURLToPath as fileURLToPath5 } from "node:url";
14604
+ import { fileURLToPath as fileURLToPath6 } from "node:url";
14348
14605
  import { EventEmitter } from "node:events";
14349
14606
  import { createInterface as createInterface2 } from "node:readline";
14350
14607
  function isAudioPath(path) {
@@ -14362,7 +14619,7 @@ function findMicCaptureCommand() {
14362
14619
  const platform4 = process.platform;
14363
14620
  if (platform4 === "linux") {
14364
14621
  try {
14365
- execSync15("which arecord", { stdio: "pipe" });
14622
+ execSync16("which arecord", { stdio: "pipe" });
14366
14623
  return {
14367
14624
  cmd: "arecord",
14368
14625
  args: ["-f", "S16_LE", "-r", "16000", "-c", "1", "-t", "raw", "-q", "-"]
@@ -14372,7 +14629,7 @@ function findMicCaptureCommand() {
14372
14629
  }
14373
14630
  if (platform4 === "darwin") {
14374
14631
  try {
14375
- execSync15("which sox", { stdio: "pipe" });
14632
+ execSync16("which sox", { stdio: "pipe" });
14376
14633
  return {
14377
14634
  cmd: "sox",
14378
14635
  args: ["-d", "-t", "raw", "-r", "16000", "-c", "1", "-b", "16", "-e", "signed-integer", "-"]
@@ -14381,7 +14638,7 @@ function findMicCaptureCommand() {
14381
14638
  }
14382
14639
  }
14383
14640
  try {
14384
- execSync15("which ffmpeg", { stdio: "pipe" });
14641
+ execSync16("which ffmpeg", { stdio: "pipe" });
14385
14642
  if (platform4 === "linux") {
14386
14643
  return {
14387
14644
  cmd: "ffmpeg",
@@ -14426,41 +14683,41 @@ function findMicCaptureCommand() {
14426
14683
  return null;
14427
14684
  }
14428
14685
  function findLiveWhisperScript() {
14429
- const thisDir = dirname8(fileURLToPath5(import.meta.url));
14686
+ const thisDir = dirname9(fileURLToPath6(import.meta.url));
14430
14687
  const candidates = [
14431
- join23(thisDir, "../../../../packages/execution/scripts/live-whisper.py"),
14432
- join23(thisDir, "../../../packages/execution/scripts/live-whisper.py"),
14433
- join23(thisDir, "../../execution/scripts/live-whisper.py"),
14688
+ join24(thisDir, "../../../../packages/execution/scripts/live-whisper.py"),
14689
+ join24(thisDir, "../../../packages/execution/scripts/live-whisper.py"),
14690
+ join24(thisDir, "../../execution/scripts/live-whisper.py"),
14434
14691
  // npm install layout — scripts bundled alongside dist
14435
- join23(thisDir, "../scripts/live-whisper.py"),
14436
- join23(thisDir, "../../scripts/live-whisper.py")
14692
+ join24(thisDir, "../scripts/live-whisper.py"),
14693
+ join24(thisDir, "../../scripts/live-whisper.py")
14437
14694
  ];
14438
14695
  for (const p of candidates) {
14439
- if (existsSync18(p))
14696
+ if (existsSync19(p))
14440
14697
  return p;
14441
14698
  }
14442
14699
  try {
14443
- const globalRoot = execSync15("npm root -g", {
14700
+ const globalRoot = execSync16("npm root -g", {
14444
14701
  encoding: "utf-8",
14445
14702
  timeout: 5e3,
14446
14703
  stdio: ["pipe", "pipe", "pipe"]
14447
14704
  }).trim();
14448
14705
  const candidates2 = [
14449
- join23(globalRoot, "open-agents-ai", "dist", "scripts", "live-whisper.py"),
14450
- join23(globalRoot, "open-agents-ai", "scripts", "live-whisper.py")
14706
+ join24(globalRoot, "open-agents-ai", "dist", "scripts", "live-whisper.py"),
14707
+ join24(globalRoot, "open-agents-ai", "scripts", "live-whisper.py")
14451
14708
  ];
14452
14709
  for (const p of candidates2) {
14453
- if (existsSync18(p))
14710
+ if (existsSync19(p))
14454
14711
  return p;
14455
14712
  }
14456
14713
  } catch {
14457
14714
  }
14458
- const nvmBase = join23(homedir8(), ".nvm", "versions", "node");
14459
- if (existsSync18(nvmBase)) {
14715
+ const nvmBase = join24(homedir8(), ".nvm", "versions", "node");
14716
+ if (existsSync19(nvmBase)) {
14460
14717
  try {
14461
14718
  for (const ver of readdirSync6(nvmBase)) {
14462
- const p = join23(nvmBase, ver, "lib", "node_modules", "open-agents-ai", "dist", "scripts", "live-whisper.py");
14463
- if (existsSync18(p))
14719
+ const p = join24(nvmBase, ver, "lib", "node_modules", "open-agents-ai", "dist", "scripts", "live-whisper.py");
14720
+ if (existsSync19(p))
14464
14721
  return p;
14465
14722
  }
14466
14723
  } catch {
@@ -14473,12 +14730,12 @@ function ensureTranscribeCliBackground() {
14473
14730
  return;
14474
14731
  _bgInstallPromise = (async () => {
14475
14732
  try {
14476
- const globalRoot = execSync15("npm root -g", {
14733
+ const globalRoot = execSync16("npm root -g", {
14477
14734
  encoding: "utf-8",
14478
14735
  timeout: 5e3,
14479
14736
  stdio: ["pipe", "pipe", "pipe"]
14480
14737
  }).trim();
14481
- if (existsSync18(join23(globalRoot, "transcribe-cli", "dist", "index.js"))) {
14738
+ if (existsSync19(join24(globalRoot, "transcribe-cli", "dist", "index.js"))) {
14482
14739
  return true;
14483
14740
  }
14484
14741
  } catch {
@@ -14544,7 +14801,7 @@ var init_listen = __esm({
14544
14801
  const timeout = setTimeout(() => {
14545
14802
  reject(new Error("Whisper fallback: model load timeout (5 min). First run downloads the model."));
14546
14803
  }, 3e5);
14547
- this.process = spawn7("python3", [
14804
+ this.process = spawn8("python3", [
14548
14805
  this.scriptPath,
14549
14806
  "--model",
14550
14807
  this.model,
@@ -14673,7 +14930,7 @@ var init_listen = __esm({
14673
14930
  }
14674
14931
  if (!this.transcribeCliAvailable) {
14675
14932
  try {
14676
- execSync15("which transcribe-cli", { stdio: "pipe" });
14933
+ execSync16("which transcribe-cli", { stdio: "pipe" });
14677
14934
  this.transcribeCliAvailable = true;
14678
14935
  } catch {
14679
14936
  this.transcribeCliAvailable = false;
@@ -14690,29 +14947,29 @@ var init_listen = __esm({
14690
14947
  } catch {
14691
14948
  }
14692
14949
  try {
14693
- const globalRoot = execSync15("npm root -g", {
14950
+ const globalRoot = execSync16("npm root -g", {
14694
14951
  encoding: "utf-8",
14695
14952
  timeout: 5e3,
14696
14953
  stdio: ["pipe", "pipe", "pipe"]
14697
14954
  }).trim();
14698
- const tcPath = join23(globalRoot, "transcribe-cli");
14699
- if (existsSync18(join23(tcPath, "dist", "index.js"))) {
14955
+ const tcPath = join24(globalRoot, "transcribe-cli");
14956
+ if (existsSync19(join24(tcPath, "dist", "index.js"))) {
14700
14957
  const { createRequire: createRequire4 } = await import("node:module");
14701
14958
  const req = createRequire4(import.meta.url);
14702
- return req(join23(tcPath, "dist", "index.js"));
14959
+ return req(join24(tcPath, "dist", "index.js"));
14703
14960
  }
14704
14961
  } catch {
14705
14962
  }
14706
- const nvmBase = join23(homedir8(), ".nvm", "versions", "node");
14707
- if (existsSync18(nvmBase)) {
14963
+ const nvmBase = join24(homedir8(), ".nvm", "versions", "node");
14964
+ if (existsSync19(nvmBase)) {
14708
14965
  try {
14709
14966
  const { readdirSync: readdirSync11 } = await import("node:fs");
14710
14967
  for (const ver of readdirSync11(nvmBase)) {
14711
- const tcPath = join23(nvmBase, ver, "lib", "node_modules", "transcribe-cli");
14712
- if (existsSync18(join23(tcPath, "dist", "index.js"))) {
14968
+ const tcPath = join24(nvmBase, ver, "lib", "node_modules", "transcribe-cli");
14969
+ if (existsSync19(join24(tcPath, "dist", "index.js"))) {
14713
14970
  const { createRequire: createRequire4 } = await import("node:module");
14714
14971
  const req = createRequire4(import.meta.url);
14715
- return req(join23(tcPath, "dist", "index.js"));
14972
+ return req(join24(tcPath, "dist", "index.js"));
14716
14973
  }
14717
14974
  }
14718
14975
  } catch {
@@ -14740,7 +14997,7 @@ var init_listen = __esm({
14740
14997
  }
14741
14998
  if (!tc) {
14742
14999
  try {
14743
- execSync15("npm i -g transcribe-cli", { stdio: "pipe", timeout: 18e4 });
15000
+ execSync16("npm i -g transcribe-cli", { stdio: "pipe", timeout: 18e4 });
14744
15001
  this.transcribeCliAvailable = null;
14745
15002
  tc = await this.loadTranscribeCli();
14746
15003
  } catch {
@@ -14826,7 +15083,7 @@ transcribe-cli error: ${transcribeCliError}` : "";
14826
15083
  return `Failed to start live transcription: ${msg}${tcHint}`;
14827
15084
  }
14828
15085
  }
14829
- this.micProcess = spawn7(micCmd.cmd, micCmd.args, {
15086
+ this.micProcess = spawn8(micCmd.cmd, micCmd.args, {
14830
15087
  stdio: ["pipe", "pipe", "pipe"],
14831
15088
  env: { ...process.env }
14832
15089
  });
@@ -14921,7 +15178,7 @@ transcribe-cli error: ${transcribeCliError}` : "";
14921
15178
  }
14922
15179
  if (!tc) {
14923
15180
  try {
14924
- execSync15("npm i -g transcribe-cli", { stdio: "pipe", timeout: 18e4 });
15181
+ execSync16("npm i -g transcribe-cli", { stdio: "pipe", timeout: 18e4 });
14925
15182
  this.transcribeCliAvailable = null;
14926
15183
  tc = await this.loadTranscribeCli();
14927
15184
  } catch {
@@ -14938,9 +15195,9 @@ transcribe-cli error: ${transcribeCliError}` : "";
14938
15195
  });
14939
15196
  if (outputDir) {
14940
15197
  const { basename: basename14 } = await import("node:path");
14941
- const transcriptDir = join23(outputDir, ".oa", "transcripts");
15198
+ const transcriptDir = join24(outputDir, ".oa", "transcripts");
14942
15199
  mkdirSync5(transcriptDir, { recursive: true });
14943
- const outFile = join23(transcriptDir, `${basename14(filePath)}.txt`);
15200
+ const outFile = join24(transcriptDir, `${basename14(filePath)}.txt`);
14944
15201
  writeFileSync5(outFile, result.text, "utf-8");
14945
15202
  }
14946
15203
  return {
@@ -16220,8 +16477,8 @@ Approach this task thoughtfully:
16220
16477
  });
16221
16478
 
16222
16479
  // packages/prompts/dist/index.js
16223
- import { join as join24, dirname as dirname9 } from "node:path";
16224
- import { fileURLToPath as fileURLToPath6 } from "node:url";
16480
+ import { join as join25, dirname as dirname10 } from "node:path";
16481
+ import { fileURLToPath as fileURLToPath7 } from "node:url";
16225
16482
  var _dir, _packageRoot;
16226
16483
  var init_dist6 = __esm({
16227
16484
  "packages/prompts/dist/index.js"() {
@@ -16230,25 +16487,25 @@ var init_dist6 = __esm({
16230
16487
  init_render2();
16231
16488
  init_task_templates();
16232
16489
  init_render2();
16233
- _dir = dirname9(fileURLToPath6(import.meta.url));
16234
- _packageRoot = join24(_dir, "..");
16490
+ _dir = dirname10(fileURLToPath7(import.meta.url));
16491
+ _packageRoot = join25(_dir, "..");
16235
16492
  }
16236
16493
  });
16237
16494
 
16238
16495
  // packages/cli/dist/tui/oa-directory.js
16239
- import { existsSync as existsSync19, mkdirSync as mkdirSync6, readFileSync as readFileSync13, writeFileSync as writeFileSync6, readdirSync as readdirSync7, statSync as statSync9, unlinkSync as unlinkSync3 } from "node:fs";
16240
- import { join as join25, relative as relative2, basename as basename9, extname as extname8 } from "node:path";
16496
+ import { existsSync as existsSync20, mkdirSync as mkdirSync6, readFileSync as readFileSync14, writeFileSync as writeFileSync6, readdirSync as readdirSync7, statSync as statSync9, unlinkSync as unlinkSync3 } from "node:fs";
16497
+ import { join as join26, relative as relative2, basename as basename9, extname as extname8 } from "node:path";
16241
16498
  import { homedir as homedir9 } from "node:os";
16242
16499
  function initOaDirectory(repoRoot) {
16243
- const oaPath = join25(repoRoot, OA_DIR);
16500
+ const oaPath = join26(repoRoot, OA_DIR);
16244
16501
  for (const sub of SUBDIRS) {
16245
- mkdirSync6(join25(oaPath, sub), { recursive: true });
16502
+ mkdirSync6(join26(oaPath, sub), { recursive: true });
16246
16503
  }
16247
16504
  try {
16248
- const gitignorePath = join25(repoRoot, ".gitignore");
16505
+ const gitignorePath = join26(repoRoot, ".gitignore");
16249
16506
  const settingsPattern = ".oa/settings.json";
16250
- if (existsSync19(gitignorePath)) {
16251
- const content = readFileSync13(gitignorePath, "utf-8");
16507
+ if (existsSync20(gitignorePath)) {
16508
+ const content = readFileSync14(gitignorePath, "utf-8");
16252
16509
  if (!content.includes(settingsPattern)) {
16253
16510
  writeFileSync6(gitignorePath, content.trimEnd() + "\n" + settingsPattern + "\n", "utf-8");
16254
16511
  }
@@ -16258,41 +16515,41 @@ function initOaDirectory(repoRoot) {
16258
16515
  return oaPath;
16259
16516
  }
16260
16517
  function hasOaDirectory(repoRoot) {
16261
- return existsSync19(join25(repoRoot, OA_DIR, "index"));
16518
+ return existsSync20(join26(repoRoot, OA_DIR, "index"));
16262
16519
  }
16263
16520
  function loadProjectSettings(repoRoot) {
16264
- const settingsPath = join25(repoRoot, OA_DIR, "settings.json");
16521
+ const settingsPath = join26(repoRoot, OA_DIR, "settings.json");
16265
16522
  try {
16266
- if (existsSync19(settingsPath)) {
16267
- return JSON.parse(readFileSync13(settingsPath, "utf-8"));
16523
+ if (existsSync20(settingsPath)) {
16524
+ return JSON.parse(readFileSync14(settingsPath, "utf-8"));
16268
16525
  }
16269
16526
  } catch {
16270
16527
  }
16271
16528
  return {};
16272
16529
  }
16273
16530
  function saveProjectSettings(repoRoot, settings) {
16274
- const oaPath = join25(repoRoot, OA_DIR);
16531
+ const oaPath = join26(repoRoot, OA_DIR);
16275
16532
  mkdirSync6(oaPath, { recursive: true });
16276
16533
  const existing = loadProjectSettings(repoRoot);
16277
16534
  const merged = { ...existing, ...settings };
16278
- writeFileSync6(join25(oaPath, "settings.json"), JSON.stringify(merged, null, 2) + "\n", { encoding: "utf-8", mode: 384 });
16535
+ writeFileSync6(join26(oaPath, "settings.json"), JSON.stringify(merged, null, 2) + "\n", { encoding: "utf-8", mode: 384 });
16279
16536
  }
16280
16537
  function loadGlobalSettings() {
16281
- const settingsPath = join25(homedir9(), ".open-agents", "settings.json");
16538
+ const settingsPath = join26(homedir9(), ".open-agents", "settings.json");
16282
16539
  try {
16283
- if (existsSync19(settingsPath)) {
16284
- return JSON.parse(readFileSync13(settingsPath, "utf-8"));
16540
+ if (existsSync20(settingsPath)) {
16541
+ return JSON.parse(readFileSync14(settingsPath, "utf-8"));
16285
16542
  }
16286
16543
  } catch {
16287
16544
  }
16288
16545
  return {};
16289
16546
  }
16290
16547
  function saveGlobalSettings(settings) {
16291
- const dir = join25(homedir9(), ".open-agents");
16548
+ const dir = join26(homedir9(), ".open-agents");
16292
16549
  mkdirSync6(dir, { recursive: true });
16293
16550
  const existing = loadGlobalSettings();
16294
16551
  const merged = { ...existing, ...settings };
16295
- writeFileSync6(join25(dir, "settings.json"), JSON.stringify(merged, null, 2) + "\n", { encoding: "utf-8", mode: 384 });
16552
+ writeFileSync6(join26(dir, "settings.json"), JSON.stringify(merged, null, 2) + "\n", { encoding: "utf-8", mode: 384 });
16296
16553
  }
16297
16554
  function resolveSettings(repoRoot) {
16298
16555
  const global = loadGlobalSettings();
@@ -16307,12 +16564,12 @@ function discoverContextFiles(repoRoot, maxContentLen = 8e3) {
16307
16564
  while (dir && !visited.has(dir)) {
16308
16565
  visited.add(dir);
16309
16566
  for (const name of CONTEXT_FILES) {
16310
- const filePath = join25(dir, name);
16567
+ const filePath = join26(dir, name);
16311
16568
  const normalizedName = name.toLowerCase();
16312
- if (existsSync19(filePath) && !seen.has(filePath)) {
16569
+ if (existsSync20(filePath) && !seen.has(filePath)) {
16313
16570
  seen.add(filePath);
16314
16571
  try {
16315
- let content = readFileSync13(filePath, "utf-8");
16572
+ let content = readFileSync14(filePath, "utf-8");
16316
16573
  if (content.length > maxContentLen) {
16317
16574
  content = content.slice(0, maxContentLen) + "\n\n...(truncated)";
16318
16575
  }
@@ -16326,11 +16583,11 @@ function discoverContextFiles(repoRoot, maxContentLen = 8e3) {
16326
16583
  }
16327
16584
  }
16328
16585
  }
16329
- const projectMap = join25(dir, OA_DIR, "context", "project-map.md");
16330
- if (existsSync19(projectMap) && !seen.has(projectMap)) {
16586
+ const projectMap = join26(dir, OA_DIR, "context", "project-map.md");
16587
+ if (existsSync20(projectMap) && !seen.has(projectMap)) {
16331
16588
  seen.add(projectMap);
16332
16589
  try {
16333
- let content = readFileSync13(projectMap, "utf-8");
16590
+ let content = readFileSync14(projectMap, "utf-8");
16334
16591
  if (content.length > maxContentLen) {
16335
16592
  content = content.slice(0, maxContentLen) + "\n\n...(truncated)";
16336
16593
  }
@@ -16342,7 +16599,7 @@ function discoverContextFiles(repoRoot, maxContentLen = 8e3) {
16342
16599
  } catch {
16343
16600
  }
16344
16601
  }
16345
- const parent = join25(dir, "..");
16602
+ const parent = join26(dir, "..");
16346
16603
  if (parent === dir)
16347
16604
  break;
16348
16605
  dir = parent;
@@ -16360,9 +16617,9 @@ function discoverContextFiles(repoRoot, maxContentLen = 8e3) {
16360
16617
  return found;
16361
16618
  }
16362
16619
  function readIndexMeta(repoRoot) {
16363
- const metaPath = join25(repoRoot, OA_DIR, "index", "meta.json");
16620
+ const metaPath = join26(repoRoot, OA_DIR, "index", "meta.json");
16364
16621
  try {
16365
- return JSON.parse(readFileSync13(metaPath, "utf-8"));
16622
+ return JSON.parse(readFileSync14(metaPath, "utf-8"));
16366
16623
  } catch {
16367
16624
  return null;
16368
16625
  }
@@ -16413,28 +16670,28 @@ ${tree}\`\`\`
16413
16670
  sections.push("");
16414
16671
  }
16415
16672
  const content = sections.join("\n");
16416
- const contextDir = join25(repoRoot, OA_DIR, "context");
16673
+ const contextDir = join26(repoRoot, OA_DIR, "context");
16417
16674
  mkdirSync6(contextDir, { recursive: true });
16418
- writeFileSync6(join25(contextDir, "project-map.md"), content, "utf-8");
16675
+ writeFileSync6(join26(contextDir, "project-map.md"), content, "utf-8");
16419
16676
  return content;
16420
16677
  }
16421
16678
  function saveSession(repoRoot, session) {
16422
- const historyDir = join25(repoRoot, OA_DIR, "history");
16679
+ const historyDir = join26(repoRoot, OA_DIR, "history");
16423
16680
  mkdirSync6(historyDir, { recursive: true });
16424
- writeFileSync6(join25(historyDir, `${session.id}.json`), JSON.stringify(session, null, 2), "utf-8");
16681
+ writeFileSync6(join26(historyDir, `${session.id}.json`), JSON.stringify(session, null, 2), "utf-8");
16425
16682
  }
16426
16683
  function loadRecentSessions(repoRoot, limit = 5) {
16427
- const historyDir = join25(repoRoot, OA_DIR, "history");
16428
- if (!existsSync19(historyDir))
16684
+ const historyDir = join26(repoRoot, OA_DIR, "history");
16685
+ if (!existsSync20(historyDir))
16429
16686
  return [];
16430
16687
  try {
16431
16688
  const files = readdirSync7(historyDir).filter((f) => f.endsWith(".json") && f !== "pending-task.json").map((f) => {
16432
- const stat5 = statSync9(join25(historyDir, f));
16689
+ const stat5 = statSync9(join26(historyDir, f));
16433
16690
  return { file: f, mtime: stat5.mtimeMs };
16434
16691
  }).sort((a, b) => b.mtime - a.mtime).slice(0, limit);
16435
16692
  return files.map((f) => {
16436
16693
  try {
16437
- return JSON.parse(readFileSync13(join25(historyDir, f.file), "utf-8"));
16694
+ return JSON.parse(readFileSync14(join26(historyDir, f.file), "utf-8"));
16438
16695
  } catch {
16439
16696
  return null;
16440
16697
  }
@@ -16444,16 +16701,16 @@ function loadRecentSessions(repoRoot, limit = 5) {
16444
16701
  }
16445
16702
  }
16446
16703
  function savePendingTask(repoRoot, task) {
16447
- const historyDir = join25(repoRoot, OA_DIR, "history");
16704
+ const historyDir = join26(repoRoot, OA_DIR, "history");
16448
16705
  mkdirSync6(historyDir, { recursive: true });
16449
- writeFileSync6(join25(historyDir, PENDING_TASK_FILE), JSON.stringify(task, null, 2) + "\n", "utf-8");
16706
+ writeFileSync6(join26(historyDir, PENDING_TASK_FILE), JSON.stringify(task, null, 2) + "\n", "utf-8");
16450
16707
  }
16451
16708
  function loadPendingTask(repoRoot) {
16452
- const filePath = join25(repoRoot, OA_DIR, "history", PENDING_TASK_FILE);
16709
+ const filePath = join26(repoRoot, OA_DIR, "history", PENDING_TASK_FILE);
16453
16710
  try {
16454
- if (!existsSync19(filePath))
16711
+ if (!existsSync20(filePath))
16455
16712
  return null;
16456
- const data = JSON.parse(readFileSync13(filePath, "utf-8"));
16713
+ const data = JSON.parse(readFileSync14(filePath, "utf-8"));
16457
16714
  try {
16458
16715
  unlinkSync3(filePath);
16459
16716
  } catch {
@@ -16464,13 +16721,13 @@ function loadPendingTask(repoRoot) {
16464
16721
  }
16465
16722
  }
16466
16723
  function saveSessionContext(repoRoot, entry) {
16467
- const contextDir = join25(repoRoot, OA_DIR, "context");
16724
+ const contextDir = join26(repoRoot, OA_DIR, "context");
16468
16725
  mkdirSync6(contextDir, { recursive: true });
16469
- const filePath = join25(contextDir, CONTEXT_SAVE_FILE);
16726
+ const filePath = join26(contextDir, CONTEXT_SAVE_FILE);
16470
16727
  let ctx;
16471
16728
  try {
16472
- if (existsSync19(filePath)) {
16473
- ctx = JSON.parse(readFileSync13(filePath, "utf-8"));
16729
+ if (existsSync20(filePath)) {
16730
+ ctx = JSON.parse(readFileSync14(filePath, "utf-8"));
16474
16731
  } else {
16475
16732
  ctx = { entries: [], maxEntries: MAX_CONTEXT_ENTRIES, updatedAt: "" };
16476
16733
  }
@@ -16485,11 +16742,11 @@ function saveSessionContext(repoRoot, entry) {
16485
16742
  writeFileSync6(filePath, JSON.stringify(ctx, null, 2) + "\n", "utf-8");
16486
16743
  }
16487
16744
  function loadSessionContext(repoRoot) {
16488
- const filePath = join25(repoRoot, OA_DIR, "context", CONTEXT_SAVE_FILE);
16745
+ const filePath = join26(repoRoot, OA_DIR, "context", CONTEXT_SAVE_FILE);
16489
16746
  try {
16490
- if (!existsSync19(filePath))
16747
+ if (!existsSync20(filePath))
16491
16748
  return null;
16492
- return JSON.parse(readFileSync13(filePath, "utf-8"));
16749
+ return JSON.parse(readFileSync14(filePath, "utf-8"));
16493
16750
  } catch {
16494
16751
  return null;
16495
16752
  }
@@ -16536,12 +16793,12 @@ function detectManifests(repoRoot) {
16536
16793
  { file: "docker-compose.yaml", type: "Docker Compose" }
16537
16794
  ];
16538
16795
  for (const check of checks) {
16539
- const filePath = join25(repoRoot, check.file);
16540
- if (existsSync19(filePath)) {
16796
+ const filePath = join26(repoRoot, check.file);
16797
+ if (existsSync20(filePath)) {
16541
16798
  let name;
16542
16799
  if (check.nameField) {
16543
16800
  try {
16544
- const data = JSON.parse(readFileSync13(filePath, "utf-8"));
16801
+ const data = JSON.parse(readFileSync14(filePath, "utf-8"));
16545
16802
  name = data[check.nameField];
16546
16803
  } catch {
16547
16804
  }
@@ -16570,7 +16827,7 @@ function findKeyFiles(repoRoot) {
16570
16827
  { pattern: "CLAUDE.md", description: "Claude Code context" }
16571
16828
  ];
16572
16829
  for (const check of checks) {
16573
- if (existsSync19(join25(repoRoot, check.pattern))) {
16830
+ if (existsSync20(join26(repoRoot, check.pattern))) {
16574
16831
  keyFiles.push({ path: check.pattern, description: check.description });
16575
16832
  }
16576
16833
  }
@@ -16596,12 +16853,12 @@ function buildDirTree(root, maxDepth, prefix = "", depth = 0) {
16596
16853
  if (entry.isDirectory()) {
16597
16854
  let fileCount = 0;
16598
16855
  try {
16599
- fileCount = readdirSync7(join25(root, entry.name)).filter((f) => !f.startsWith(".")).length;
16856
+ fileCount = readdirSync7(join26(root, entry.name)).filter((f) => !f.startsWith(".")).length;
16600
16857
  } catch {
16601
16858
  }
16602
16859
  result += `${prefix}${connector}${entry.name}/ (${fileCount})
16603
16860
  `;
16604
- result += buildDirTree(join25(root, entry.name), maxDepth, childPrefix, depth + 1);
16861
+ result += buildDirTree(join26(root, entry.name), maxDepth, childPrefix, depth + 1);
16605
16862
  } else if (depth < maxDepth) {
16606
16863
  result += `${prefix}${connector}${entry.name}
16607
16864
  `;
@@ -16654,9 +16911,9 @@ var init_oa_directory = __esm({
16654
16911
 
16655
16912
  // packages/cli/dist/tui/setup.js
16656
16913
  import * as readline from "node:readline";
16657
- import { execSync as execSync16, spawn as spawn8 } from "node:child_process";
16658
- import { existsSync as existsSync20, writeFileSync as writeFileSync7, mkdirSync as mkdirSync7 } from "node:fs";
16659
- import { join as join26 } from "node:path";
16914
+ import { execSync as execSync17, spawn as spawn9 } from "node:child_process";
16915
+ import { existsSync as existsSync21, writeFileSync as writeFileSync7, mkdirSync as mkdirSync7 } from "node:fs";
16916
+ import { join as join27 } from "node:path";
16660
16917
  import { homedir as homedir10, platform } from "node:os";
16661
16918
  function detectSystemSpecs() {
16662
16919
  let totalRamGB = 0;
@@ -16664,7 +16921,7 @@ function detectSystemSpecs() {
16664
16921
  let gpuVramGB = 0;
16665
16922
  let gpuName = "";
16666
16923
  try {
16667
- const memInfo = execSync16("free -b 2>/dev/null || sysctl -n hw.memsize 2>/dev/null", {
16924
+ const memInfo = execSync17("free -b 2>/dev/null || sysctl -n hw.memsize 2>/dev/null", {
16668
16925
  encoding: "utf8",
16669
16926
  timeout: 5e3
16670
16927
  });
@@ -16684,7 +16941,7 @@ function detectSystemSpecs() {
16684
16941
  } catch {
16685
16942
  }
16686
16943
  try {
16687
- const nvidiaSmi = execSync16("nvidia-smi --query-gpu=memory.total,name --format=csv,noheader,nounits 2>/dev/null", { encoding: "utf8", timeout: 5e3 });
16944
+ const nvidiaSmi = execSync17("nvidia-smi --query-gpu=memory.total,name --format=csv,noheader,nounits 2>/dev/null", { encoding: "utf8", timeout: 5e3 });
16688
16945
  const lines = nvidiaSmi.trim().split("\n");
16689
16946
  if (lines.length > 0) {
16690
16947
  for (const line of lines) {
@@ -16809,7 +17066,7 @@ function installOllamaLinux() {
16809
17066
 
16810
17067
  `);
16811
17068
  try {
16812
- execSync16("curl -fsSL https://ollama.com/install.sh | sh", {
17069
+ execSync17("curl -fsSL https://ollama.com/install.sh | sh", {
16813
17070
  stdio: "inherit",
16814
17071
  timeout: 3e5
16815
17072
  });
@@ -16848,10 +17105,10 @@ async function installOllamaMac(rl) {
16848
17105
 
16849
17106
  `);
16850
17107
  try {
16851
- execSync16('/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"', { stdio: "inherit", timeout: 6e5 });
17108
+ execSync17('/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"', { stdio: "inherit", timeout: 6e5 });
16852
17109
  if (!hasCmd("brew")) {
16853
17110
  try {
16854
- const brewPrefix = existsSync20("/opt/homebrew/bin/brew") ? "/opt/homebrew" : "/usr/local";
17111
+ const brewPrefix = existsSync21("/opt/homebrew/bin/brew") ? "/opt/homebrew" : "/usr/local";
16855
17112
  process.env["PATH"] = `${brewPrefix}/bin:${process.env["PATH"]}`;
16856
17113
  } catch {
16857
17114
  }
@@ -16881,7 +17138,7 @@ async function installOllamaMac(rl) {
16881
17138
 
16882
17139
  `);
16883
17140
  try {
16884
- execSync16("brew install ollama", {
17141
+ execSync17("brew install ollama", {
16885
17142
  stdio: "inherit",
16886
17143
  timeout: 3e5
16887
17144
  });
@@ -16908,7 +17165,7 @@ function installOllamaWindows() {
16908
17165
 
16909
17166
  `);
16910
17167
  try {
16911
- execSync16('powershell -Command "irm https://ollama.com/install.ps1 | iex"', {
17168
+ execSync17('powershell -Command "irm https://ollama.com/install.ps1 | iex"', {
16912
17169
  stdio: "inherit",
16913
17170
  timeout: 3e5
16914
17171
  });
@@ -16931,7 +17188,7 @@ function installOllamaWindows() {
16931
17188
  }
16932
17189
  function pullModelWithAutoUpdate(tag) {
16933
17190
  try {
16934
- execSync16(`ollama pull ${tag}`, {
17191
+ execSync17(`ollama pull ${tag}`, {
16935
17192
  stdio: "inherit",
16936
17193
  timeout: 36e5
16937
17194
  // 1 hour max
@@ -16948,7 +17205,7 @@ function pullModelWithAutoUpdate(tag) {
16948
17205
 
16949
17206
  `);
16950
17207
  try {
16951
- execSync16("curl -fsSL https://ollama.com/install.sh | sh", {
17208
+ execSync17("curl -fsSL https://ollama.com/install.sh | sh", {
16952
17209
  stdio: "inherit",
16953
17210
  timeout: 3e5
16954
17211
  // 5 min max for install
@@ -16959,7 +17216,7 @@ function pullModelWithAutoUpdate(tag) {
16959
17216
  process.stdout.write(` ${c2.cyan("\u25CF")} Retrying pull of ${c2.bold(tag)}...
16960
17217
 
16961
17218
  `);
16962
- execSync16(`ollama pull ${tag}`, {
17219
+ execSync17(`ollama pull ${tag}`, {
16963
17220
  stdio: "inherit",
16964
17221
  timeout: 36e5
16965
17222
  });
@@ -17140,7 +17397,7 @@ async function doSetup(config, rl) {
17140
17397
  ${c2.cyan("\u25CF")} Starting ollama serve in background...
17141
17398
  `);
17142
17399
  try {
17143
- const child = spawn8("ollama", ["serve"], { stdio: "ignore", detached: true });
17400
+ const child = spawn9("ollama", ["serve"], { stdio: "ignore", detached: true });
17144
17401
  child.unref();
17145
17402
  await new Promise((resolve23) => setTimeout(resolve23, 3e3));
17146
17403
  try {
@@ -17168,7 +17425,7 @@ async function doSetup(config, rl) {
17168
17425
  ${c2.cyan("\u25CF")} Starting ollama serve...
17169
17426
  `);
17170
17427
  try {
17171
- const child = spawn8("ollama", ["serve"], { stdio: "ignore", detached: true });
17428
+ const child = spawn9("ollama", ["serve"], { stdio: "ignore", detached: true });
17172
17429
  child.unref();
17173
17430
  await new Promise((resolve23) => setTimeout(resolve23, 3e3));
17174
17431
  try {
@@ -17325,12 +17582,12 @@ async function doSetup(config, rl) {
17325
17582
  `PARAMETER num_predict ${numPredict}`,
17326
17583
  `PARAMETER stop "<|endoftext|>"`
17327
17584
  ].join("\n");
17328
- const modelDir2 = join26(homedir10(), ".open-agents", "models");
17585
+ const modelDir2 = join27(homedir10(), ".open-agents", "models");
17329
17586
  mkdirSync7(modelDir2, { recursive: true });
17330
- const modelfilePath = join26(modelDir2, `Modelfile.${customName}`);
17587
+ const modelfilePath = join27(modelDir2, `Modelfile.${customName}`);
17331
17588
  writeFileSync7(modelfilePath, modelfileContent + "\n", "utf8");
17332
17589
  process.stdout.write(` ${c2.dim("Creating model...")} `);
17333
- execSync16(`ollama create ${customName} -f ${modelfilePath}`, {
17590
+ execSync17(`ollama create ${customName} -f ${modelfilePath}`, {
17334
17591
  stdio: "pipe",
17335
17592
  timeout: 12e4
17336
17593
  });
@@ -17373,7 +17630,7 @@ async function isModelAvailable(config) {
17373
17630
  }
17374
17631
  function isFirstRun() {
17375
17632
  try {
17376
- return !existsSync20(join26(homedir10(), ".open-agents", "config.json"));
17633
+ return !existsSync21(join27(homedir10(), ".open-agents", "config.json"));
17377
17634
  } catch {
17378
17635
  return true;
17379
17636
  }
@@ -17381,7 +17638,7 @@ function isFirstRun() {
17381
17638
  function hasCmd(cmd) {
17382
17639
  try {
17383
17640
  const whichCmd = process.platform === "win32" ? `where ${cmd}` : `which ${cmd}`;
17384
- execSync16(whichCmd, { stdio: "pipe", timeout: 3e3 });
17641
+ execSync17(whichCmd, { stdio: "pipe", timeout: 3e3 });
17385
17642
  return true;
17386
17643
  } catch {
17387
17644
  return false;
@@ -17410,11 +17667,11 @@ function detectPkgManager() {
17410
17667
  return null;
17411
17668
  }
17412
17669
  function getVenvDir() {
17413
- return join26(homedir10(), ".open-agents", "venv");
17670
+ return join27(homedir10(), ".open-agents", "venv");
17414
17671
  }
17415
17672
  function hasVenvModule() {
17416
17673
  try {
17417
- execSync16("python3 -m venv --help", { stdio: "pipe", timeout: 5e3 });
17674
+ execSync17("python3 -m venv --help", { stdio: "pipe", timeout: 5e3 });
17418
17675
  return true;
17419
17676
  } catch {
17420
17677
  return false;
@@ -17422,8 +17679,8 @@ function hasVenvModule() {
17422
17679
  }
17423
17680
  function ensureVenv(log) {
17424
17681
  const venvDir = getVenvDir();
17425
- const venvPip = join26(venvDir, "bin", "pip");
17426
- if (existsSync20(venvPip))
17682
+ const venvPip = join27(venvDir, "bin", "pip");
17683
+ if (existsSync21(venvPip))
17427
17684
  return venvDir;
17428
17685
  log("Creating Python venv for vision deps...");
17429
17686
  if (!hasCmd("python3")) {
@@ -17435,9 +17692,9 @@ function ensureVenv(log) {
17435
17692
  return null;
17436
17693
  }
17437
17694
  try {
17438
- mkdirSync7(join26(homedir10(), ".open-agents"), { recursive: true });
17439
- execSync16(`python3 -m venv "${venvDir}"`, { stdio: "pipe", timeout: 3e4 });
17440
- execSync16(`"${join26(venvDir, "bin", "pip")}" install --upgrade pip`, {
17695
+ mkdirSync7(join27(homedir10(), ".open-agents"), { recursive: true });
17696
+ execSync17(`python3 -m venv "${venvDir}"`, { stdio: "pipe", timeout: 3e4 });
17697
+ execSync17(`"${join27(venvDir, "bin", "pip")}" install --upgrade pip`, {
17441
17698
  stdio: "pipe",
17442
17699
  timeout: 6e4
17443
17700
  });
@@ -17450,7 +17707,7 @@ function ensureVenv(log) {
17450
17707
  }
17451
17708
  function trySudoPasswordless(cmd, timeoutMs = 12e4) {
17452
17709
  try {
17453
- execSync16(`sudo -n ${cmd}`, {
17710
+ execSync17(`sudo -n ${cmd}`, {
17454
17711
  stdio: "pipe",
17455
17712
  timeout: timeoutMs,
17456
17713
  env: { ...process.env, DEBIAN_FRONTEND: "noninteractive" }
@@ -17462,7 +17719,7 @@ function trySudoPasswordless(cmd, timeoutMs = 12e4) {
17462
17719
  }
17463
17720
  function runWithSudo(cmd, password, timeoutMs = 12e4) {
17464
17721
  try {
17465
- execSync16(`sudo -S ${cmd}`, {
17722
+ execSync17(`sudo -S ${cmd}`, {
17466
17723
  input: password + "\n",
17467
17724
  stdio: ["pipe", "pipe", "pipe"],
17468
17725
  timeout: timeoutMs,
@@ -17522,7 +17779,7 @@ async function ensureVisionDeps(onInfo, getSudoPassword) {
17522
17779
  } else {
17523
17780
  log("Installing tesseract-ocr...");
17524
17781
  try {
17525
- execSync16(pkg.cmd, { stdio: "pipe", timeout: 12e4 });
17782
+ execSync17(pkg.cmd, { stdio: "pipe", timeout: 12e4 });
17526
17783
  if (hasCmd("tesseract")) {
17527
17784
  log("tesseract-ocr installed successfully.");
17528
17785
  } else {
@@ -17594,7 +17851,7 @@ async function ensureVisionDeps(onInfo, getSudoPassword) {
17594
17851
  } else {
17595
17852
  log(`Installing ${dep.label}...`);
17596
17853
  try {
17597
- execSync16(pkg.cmd, { stdio: "pipe", timeout: 12e4 });
17854
+ execSync17(pkg.cmd, { stdio: "pipe", timeout: 12e4 });
17598
17855
  if (hasCmd(dep.binary)) {
17599
17856
  log(`${dep.label} installed successfully.`);
17600
17857
  } else {
@@ -17625,7 +17882,7 @@ async function ensureVisionDeps(onInfo, getSudoPassword) {
17625
17882
  const venvCmds = {
17626
17883
  apt: () => {
17627
17884
  try {
17628
- const pyVer = execSync16(`python3 -c "import sys; print(f'{sys.version_info.major}.{sys.version_info.minor}')"`, { encoding: "utf8", stdio: "pipe", timeout: 5e3 }).trim();
17885
+ const pyVer = execSync17(`python3 -c "import sys; print(f'{sys.version_info.major}.{sys.version_info.minor}')"`, { encoding: "utf8", stdio: "pipe", timeout: 5e3 }).trim();
17629
17886
  return `apt-get install -y python3-venv python${pyVer}-venv`;
17630
17887
  } catch {
17631
17888
  return "apt-get install -y python3-venv";
@@ -17646,19 +17903,19 @@ async function ensureVisionDeps(onInfo, getSudoPassword) {
17646
17903
  }
17647
17904
  }
17648
17905
  const venvDir = getVenvDir();
17649
- const venvBin = join26(venvDir, "bin");
17650
- const venvMoondream = join26(venvBin, "moondream-station");
17906
+ const venvBin = join27(venvDir, "bin");
17907
+ const venvMoondream = join27(venvBin, "moondream-station");
17651
17908
  const venv = ensureVenv(log);
17652
- if (venv && !hasCmd("moondream-station") && !existsSync20(venvMoondream)) {
17653
- const venvPip = join26(venvBin, "pip");
17909
+ if (venv && !hasCmd("moondream-station") && !existsSync21(venvMoondream)) {
17910
+ const venvPip = join27(venvBin, "pip");
17654
17911
  log("Installing moondream-station in ~/.open-agents/venv...");
17655
17912
  try {
17656
- execSync16(`"${venvPip}" install moondream-station`, { stdio: "pipe", timeout: 3e5 });
17657
- if (existsSync20(venvMoondream)) {
17913
+ execSync17(`"${venvPip}" install moondream-station`, { stdio: "pipe", timeout: 3e5 });
17914
+ if (existsSync21(venvMoondream)) {
17658
17915
  log("moondream-station installed successfully.");
17659
17916
  } else {
17660
17917
  try {
17661
- const check = execSync16(`"${venvPip}" show moondream-station`, { encoding: "utf8", stdio: "pipe", timeout: 5e3 });
17918
+ const check = execSync17(`"${venvPip}" show moondream-station`, { encoding: "utf8", stdio: "pipe", timeout: 5e3 });
17662
17919
  if (check.includes("moondream")) {
17663
17920
  log("moondream-station package installed.");
17664
17921
  }
@@ -17671,11 +17928,11 @@ async function ensureVisionDeps(onInfo, getSudoPassword) {
17671
17928
  }
17672
17929
  }
17673
17930
  if (venv) {
17674
- const venvPython = join26(venvBin, "python");
17675
- const venvPip2 = join26(venvBin, "pip");
17931
+ const venvPython = join27(venvBin, "python");
17932
+ const venvPip2 = join27(venvBin, "pip");
17676
17933
  let ocrStackInstalled = false;
17677
17934
  try {
17678
- execSync16(`"${venvPython}" -c "import cv2, pytesseract, numpy, PIL"`, { stdio: "pipe", timeout: 1e4 });
17935
+ execSync17(`"${venvPython}" -c "import cv2, pytesseract, numpy, PIL"`, { stdio: "pipe", timeout: 1e4 });
17679
17936
  ocrStackInstalled = true;
17680
17937
  } catch {
17681
17938
  }
@@ -17683,9 +17940,9 @@ async function ensureVisionDeps(onInfo, getSudoPassword) {
17683
17940
  const ocrPackages = "pytesseract Pillow opencv-python-headless numpy";
17684
17941
  log("Installing OCR Python stack (pytesseract, OpenCV, Pillow, numpy)...");
17685
17942
  try {
17686
- execSync16(`"${venvPip2}" install ${ocrPackages}`, { stdio: "pipe", timeout: 3e5 });
17943
+ execSync17(`"${venvPip2}" install ${ocrPackages}`, { stdio: "pipe", timeout: 3e5 });
17687
17944
  try {
17688
- execSync16(`"${venvPython}" -c "import cv2, pytesseract, numpy, PIL"`, { stdio: "pipe", timeout: 1e4 });
17945
+ execSync17(`"${venvPython}" -c "import cv2, pytesseract, numpy, PIL"`, { stdio: "pipe", timeout: 1e4 });
17689
17946
  log("OCR Python stack installed successfully.");
17690
17947
  } catch {
17691
17948
  log("OCR Python stack install completed but import verification failed.");
@@ -17732,11 +17989,11 @@ function createExpandedVariant(baseModel, specs, sizeGB) {
17732
17989
  `PARAMETER num_predict ${numPredict}`,
17733
17990
  `PARAMETER stop "<|endoftext|>"`
17734
17991
  ].join("\n");
17735
- const modelDir2 = join26(homedir10(), ".open-agents", "models");
17992
+ const modelDir2 = join27(homedir10(), ".open-agents", "models");
17736
17993
  mkdirSync7(modelDir2, { recursive: true });
17737
- const modelfilePath = join26(modelDir2, `Modelfile.${customName}`);
17994
+ const modelfilePath = join27(modelDir2, `Modelfile.${customName}`);
17738
17995
  writeFileSync7(modelfilePath, modelfileContent + "\n", "utf8");
17739
- execSync16(`ollama create ${customName} -f ${modelfilePath}`, {
17996
+ execSync17(`ollama create ${customName} -f ${modelfilePath}`, {
17740
17997
  stdio: "pipe",
17741
17998
  timeout: 12e4
17742
17999
  });
@@ -18522,18 +18779,18 @@ async function handleUpdate(subcommand, ctx) {
18522
18779
  let currentVersion = "0.0.0";
18523
18780
  try {
18524
18781
  const { createRequire: createRequire4 } = await import("node:module");
18525
- const { fileURLToPath: fileURLToPath9 } = await import("node:url");
18526
- const { dirname: dirname12, join: join37 } = await import("node:path");
18527
- const { existsSync: existsSync27 } = await import("node:fs");
18782
+ const { fileURLToPath: fileURLToPath10 } = await import("node:url");
18783
+ const { dirname: dirname13, join: join38 } = await import("node:path");
18784
+ const { existsSync: existsSync28 } = await import("node:fs");
18528
18785
  const req = createRequire4(import.meta.url);
18529
- const thisDir = dirname12(fileURLToPath9(import.meta.url));
18786
+ const thisDir = dirname13(fileURLToPath10(import.meta.url));
18530
18787
  const candidates = [
18531
- join37(thisDir, "..", "package.json"),
18532
- join37(thisDir, "..", "..", "package.json"),
18533
- join37(thisDir, "..", "..", "..", "package.json")
18788
+ join38(thisDir, "..", "package.json"),
18789
+ join38(thisDir, "..", "..", "package.json"),
18790
+ join38(thisDir, "..", "..", "..", "package.json")
18534
18791
  ];
18535
18792
  for (const pkgPath of candidates) {
18536
- if (existsSync27(pkgPath)) {
18793
+ if (existsSync28(pkgPath)) {
18537
18794
  const pkg = req(pkgPath);
18538
18795
  if (pkg.name === "open-agents-ai" || pkg.name === "@open-agents/cli") {
18539
18796
  currentVersion = pkg.version ?? "0.0.0";
@@ -18665,9 +18922,9 @@ var init_commands = __esm({
18665
18922
  });
18666
18923
 
18667
18924
  // packages/cli/dist/tui/project-context.js
18668
- import { existsSync as existsSync21, readFileSync as readFileSync14, readdirSync as readdirSync8 } from "node:fs";
18669
- import { join as join27, basename as basename10 } from "node:path";
18670
- import { execSync as execSync17 } from "node:child_process";
18925
+ import { existsSync as existsSync22, readFileSync as readFileSync15, readdirSync as readdirSync8 } from "node:fs";
18926
+ import { join as join28, basename as basename10 } from "node:path";
18927
+ import { execSync as execSync18 } from "node:child_process";
18671
18928
  import { homedir as homedir11, platform as platform2, release } from "node:os";
18672
18929
  function getModelTier(modelName) {
18673
18930
  const m = modelName.toLowerCase();
@@ -18701,10 +18958,10 @@ function loadProjectMap(repoRoot) {
18701
18958
  if (!hasOaDirectory(repoRoot)) {
18702
18959
  initOaDirectory(repoRoot);
18703
18960
  }
18704
- const mapPath = join27(repoRoot, OA_DIR, "context", "project-map.md");
18705
- if (existsSync21(mapPath)) {
18961
+ const mapPath = join28(repoRoot, OA_DIR, "context", "project-map.md");
18962
+ if (existsSync22(mapPath)) {
18706
18963
  try {
18707
- const content = readFileSync14(mapPath, "utf-8");
18964
+ const content = readFileSync15(mapPath, "utf-8");
18708
18965
  return content;
18709
18966
  } catch {
18710
18967
  }
@@ -18713,19 +18970,19 @@ function loadProjectMap(repoRoot) {
18713
18970
  }
18714
18971
  function getGitInfo(repoRoot) {
18715
18972
  try {
18716
- execSync17("git rev-parse --is-inside-work-tree", { cwd: repoRoot, stdio: "pipe" });
18973
+ execSync18("git rev-parse --is-inside-work-tree", { cwd: repoRoot, stdio: "pipe" });
18717
18974
  } catch {
18718
18975
  return "";
18719
18976
  }
18720
18977
  const lines = [];
18721
18978
  try {
18722
- const branch = execSync17("git branch --show-current", { cwd: repoRoot, encoding: "utf-8", stdio: "pipe" }).trim();
18979
+ const branch = execSync18("git branch --show-current", { cwd: repoRoot, encoding: "utf-8", stdio: "pipe" }).trim();
18723
18980
  if (branch)
18724
18981
  lines.push(`Branch: ${branch}`);
18725
18982
  } catch {
18726
18983
  }
18727
18984
  try {
18728
- const status = execSync17("git status --porcelain", { cwd: repoRoot, encoding: "utf-8", stdio: "pipe" }).trim();
18985
+ const status = execSync18("git status --porcelain", { cwd: repoRoot, encoding: "utf-8", stdio: "pipe" }).trim();
18729
18986
  if (status) {
18730
18987
  const changed = status.split("\n").length;
18731
18988
  lines.push(`Working tree: ${changed} changed file(s)`);
@@ -18735,7 +18992,7 @@ function getGitInfo(repoRoot) {
18735
18992
  } catch {
18736
18993
  }
18737
18994
  try {
18738
- const log = execSync17("git log --oneline -5 --no-decorate", { cwd: repoRoot, encoding: "utf-8", stdio: "pipe" }).trim();
18995
+ const log = execSync18("git log --oneline -5 --no-decorate", { cwd: repoRoot, encoding: "utf-8", stdio: "pipe" }).trim();
18739
18996
  if (log)
18740
18997
  lines.push(`Recent commits:
18741
18998
  ${log}`);
@@ -18745,31 +19002,31 @@ ${log}`);
18745
19002
  }
18746
19003
  function loadMemoryContext(repoRoot) {
18747
19004
  const sections = [];
18748
- const oaMemDir = join27(repoRoot, OA_DIR, "memory");
19005
+ const oaMemDir = join28(repoRoot, OA_DIR, "memory");
18749
19006
  const oaEntries = loadMemoryDir(oaMemDir, "project");
18750
19007
  if (oaEntries)
18751
19008
  sections.push(oaEntries);
18752
- const legacyMemDir = join27(repoRoot, ".open-agents", "memory");
18753
- if (legacyMemDir !== oaMemDir && existsSync21(legacyMemDir)) {
19009
+ const legacyMemDir = join28(repoRoot, ".open-agents", "memory");
19010
+ if (legacyMemDir !== oaMemDir && existsSync22(legacyMemDir)) {
18754
19011
  const legacyEntries = loadMemoryDir(legacyMemDir, "project/legacy");
18755
19012
  if (legacyEntries)
18756
19013
  sections.push(legacyEntries);
18757
19014
  }
18758
- const globalMemDir = join27(homedir11(), ".open-agents", "memory");
19015
+ const globalMemDir = join28(homedir11(), ".open-agents", "memory");
18759
19016
  const globalEntries = loadMemoryDir(globalMemDir, "global");
18760
19017
  if (globalEntries)
18761
19018
  sections.push(globalEntries);
18762
19019
  return sections.join("\n\n");
18763
19020
  }
18764
19021
  function loadMemoryDir(memDir, scope) {
18765
- if (!existsSync21(memDir))
19022
+ if (!existsSync22(memDir))
18766
19023
  return "";
18767
19024
  const lines = [];
18768
19025
  try {
18769
19026
  const files = readdirSync8(memDir).filter((f) => f.endsWith(".json"));
18770
19027
  for (const file of files.slice(0, 10)) {
18771
19028
  try {
18772
- const raw = readFileSync14(join27(memDir, file), "utf-8");
19029
+ const raw = readFileSync15(join28(memDir, file), "utf-8");
18773
19030
  const entries = JSON.parse(raw);
18774
19031
  const topic = basename10(file, ".json");
18775
19032
  const keys = Object.keys(entries);
@@ -19796,22 +20053,22 @@ var init_carousel = __esm({
19796
20053
  });
19797
20054
 
19798
20055
  // packages/cli/dist/tui/carousel-descriptors.js
19799
- import { existsSync as existsSync22, readFileSync as readFileSync15, writeFileSync as writeFileSync8, mkdirSync as mkdirSync8, readdirSync as readdirSync9 } from "node:fs";
19800
- import { join as join28, basename as basename11 } from "node:path";
20056
+ import { existsSync as existsSync23, readFileSync as readFileSync16, writeFileSync as writeFileSync8, mkdirSync as mkdirSync8, readdirSync as readdirSync9 } from "node:fs";
20057
+ import { join as join29, basename as basename11 } from "node:path";
19801
20058
  function loadToolProfile(repoRoot) {
19802
- const filePath = join28(repoRoot, OA_DIR, "context", TOOL_PROFILE_FILE);
20059
+ const filePath = join29(repoRoot, OA_DIR, "context", TOOL_PROFILE_FILE);
19803
20060
  try {
19804
- if (!existsSync22(filePath))
20061
+ if (!existsSync23(filePath))
19805
20062
  return null;
19806
- return JSON.parse(readFileSync15(filePath, "utf-8"));
20063
+ return JSON.parse(readFileSync16(filePath, "utf-8"));
19807
20064
  } catch {
19808
20065
  return null;
19809
20066
  }
19810
20067
  }
19811
20068
  function saveToolProfile(repoRoot, profile) {
19812
- const contextDir = join28(repoRoot, OA_DIR, "context");
20069
+ const contextDir = join29(repoRoot, OA_DIR, "context");
19813
20070
  mkdirSync8(contextDir, { recursive: true });
19814
- writeFileSync8(join28(contextDir, TOOL_PROFILE_FILE), JSON.stringify(profile, null, 2), "utf-8");
20071
+ writeFileSync8(join29(contextDir, TOOL_PROFILE_FILE), JSON.stringify(profile, null, 2), "utf-8");
19815
20072
  }
19816
20073
  function categorizeToolCall(toolName) {
19817
20074
  for (const cat of TOOL_CATEGORIES) {
@@ -19869,25 +20126,25 @@ function weightedColor(profile) {
19869
20126
  return selectedCat.colors[Math.floor(Math.random() * selectedCat.colors.length)];
19870
20127
  }
19871
20128
  function loadCachedDescriptors(repoRoot) {
19872
- const filePath = join28(repoRoot, OA_DIR, "context", DESCRIPTOR_FILE);
20129
+ const filePath = join29(repoRoot, OA_DIR, "context", DESCRIPTOR_FILE);
19873
20130
  try {
19874
- if (!existsSync22(filePath))
20131
+ if (!existsSync23(filePath))
19875
20132
  return null;
19876
- const cached = JSON.parse(readFileSync15(filePath, "utf-8"));
20133
+ const cached = JSON.parse(readFileSync16(filePath, "utf-8"));
19877
20134
  return cached.phrases.length > 0 ? cached.phrases : null;
19878
20135
  } catch {
19879
20136
  return null;
19880
20137
  }
19881
20138
  }
19882
20139
  function saveCachedDescriptors(repoRoot, phrases, sourceHash) {
19883
- const contextDir = join28(repoRoot, OA_DIR, "context");
20140
+ const contextDir = join29(repoRoot, OA_DIR, "context");
19884
20141
  mkdirSync8(contextDir, { recursive: true });
19885
20142
  const cached = {
19886
20143
  phrases,
19887
20144
  generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
19888
20145
  sourceHash
19889
20146
  };
19890
- writeFileSync8(join28(contextDir, DESCRIPTOR_FILE), JSON.stringify(cached, null, 2), "utf-8");
20147
+ writeFileSync8(join29(contextDir, DESCRIPTOR_FILE), JSON.stringify(cached, null, 2), "utf-8");
19891
20148
  }
19892
20149
  function generateDescriptors(repoRoot) {
19893
20150
  const profile = loadToolProfile(repoRoot);
@@ -19935,11 +20192,11 @@ function generateDescriptors(repoRoot) {
19935
20192
  return phrases;
19936
20193
  }
19937
20194
  function extractFromPackageJson(repoRoot, tags) {
19938
- const pkgPath = join28(repoRoot, "package.json");
20195
+ const pkgPath = join29(repoRoot, "package.json");
19939
20196
  try {
19940
- if (!existsSync22(pkgPath))
20197
+ if (!existsSync23(pkgPath))
19941
20198
  return;
19942
- const pkg = JSON.parse(readFileSync15(pkgPath, "utf-8"));
20199
+ const pkg = JSON.parse(readFileSync16(pkgPath, "utf-8"));
19943
20200
  if (pkg.name && typeof pkg.name === "string") {
19944
20201
  const parts = pkg.name.replace(/^@/, "").split("/");
19945
20202
  for (const p of parts)
@@ -19983,7 +20240,7 @@ function extractFromManifests(repoRoot, tags) {
19983
20240
  { file: ".github/workflows", tag: "ci/cd" }
19984
20241
  ];
19985
20242
  for (const check of manifestChecks) {
19986
- if (existsSync22(join28(repoRoot, check.file))) {
20243
+ if (existsSync23(join29(repoRoot, check.file))) {
19987
20244
  tags.push(check.tag);
19988
20245
  }
19989
20246
  }
@@ -20005,16 +20262,16 @@ function extractFromSessions(repoRoot, tags) {
20005
20262
  }
20006
20263
  }
20007
20264
  function extractFromMemory(repoRoot, tags) {
20008
- const memoryDir = join28(repoRoot, OA_DIR, "memory");
20265
+ const memoryDir = join29(repoRoot, OA_DIR, "memory");
20009
20266
  try {
20010
- if (!existsSync22(memoryDir))
20267
+ if (!existsSync23(memoryDir))
20011
20268
  return;
20012
20269
  const files = readdirSync9(memoryDir).filter((f) => f.endsWith(".json"));
20013
20270
  for (const file of files) {
20014
20271
  const topic = file.replace(/\.json$/, "").replace(/[-_]/g, " ");
20015
20272
  tags.push(topic);
20016
20273
  try {
20017
- const data = JSON.parse(readFileSync15(join28(memoryDir, file), "utf-8"));
20274
+ const data = JSON.parse(readFileSync16(join29(memoryDir, file), "utf-8"));
20018
20275
  if (data && typeof data === "object") {
20019
20276
  const keys = Object.keys(data).slice(0, 3);
20020
20277
  for (const key of keys) {
@@ -20149,25 +20406,25 @@ var init_carousel_descriptors = __esm({
20149
20406
  });
20150
20407
 
20151
20408
  // packages/cli/dist/tui/voice.js
20152
- import { existsSync as existsSync23, mkdirSync as mkdirSync9, writeFileSync as writeFileSync9, readFileSync as readFileSync16, unlinkSync as unlinkSync4 } from "node:fs";
20153
- import { join as join29 } from "node:path";
20409
+ import { existsSync as existsSync24, mkdirSync as mkdirSync9, writeFileSync as writeFileSync9, readFileSync as readFileSync17, unlinkSync as unlinkSync4 } from "node:fs";
20410
+ import { join as join30 } from "node:path";
20154
20411
  import { homedir as homedir12, tmpdir as tmpdir6, platform as platform3 } from "node:os";
20155
- import { execSync as execSync18, spawn as nodeSpawn } from "node:child_process";
20412
+ import { execSync as execSync19, spawn as nodeSpawn } from "node:child_process";
20156
20413
  import { createRequire } from "node:module";
20157
20414
  function voiceDir() {
20158
- return join29(homedir12(), ".open-agents", "voice");
20415
+ return join30(homedir12(), ".open-agents", "voice");
20159
20416
  }
20160
20417
  function modelsDir() {
20161
- return join29(voiceDir(), "models");
20418
+ return join30(voiceDir(), "models");
20162
20419
  }
20163
20420
  function modelDir(id) {
20164
- return join29(modelsDir(), id);
20421
+ return join30(modelsDir(), id);
20165
20422
  }
20166
20423
  function modelOnnxPath(id) {
20167
- return join29(modelDir(id), "model.onnx");
20424
+ return join30(modelDir(id), "model.onnx");
20168
20425
  }
20169
20426
  function modelConfigPath(id) {
20170
- return join29(modelDir(id), "config.json");
20427
+ return join30(modelDir(id), "config.json");
20171
20428
  }
20172
20429
  function describeToolCall(toolName, args, personality = 2) {
20173
20430
  const path = args["path"];
@@ -20242,6 +20499,8 @@ function describeToolCallTerse(toolName, args, file) {
20242
20499
  return `Creating custom tool`;
20243
20500
  case "manage_tools":
20244
20501
  return `Managing custom tools`;
20502
+ case "browser_action":
20503
+ return `Browser ${args["action"] ?? "action"}`;
20245
20504
  default:
20246
20505
  return `Using ${toolName}`;
20247
20506
  }
@@ -20288,6 +20547,8 @@ function describeToolCallConversational(toolName, args, file) {
20288
20547
  return `Taking a look at that image`;
20289
20548
  case "screenshot":
20290
20549
  return `Grabbing a screenshot`;
20550
+ case "browser_action":
20551
+ return `Let me ${args["action"] === "navigate" ? "open that page" : args["action"] === "screenshot" ? "grab a screenshot of the browser" : `do a browser ${args["action"] ?? "action"}`}`;
20291
20552
  default:
20292
20553
  return `Working with ${toolName}`;
20293
20554
  }
@@ -20334,6 +20595,8 @@ function describeToolCallChatty(toolName, args, file) {
20334
20595
  return `Let me get a good look at that image`;
20335
20596
  case "screenshot":
20336
20597
  return `Snapping a screenshot to see what's happening`;
20598
+ case "browser_action":
20599
+ return `Firing up the browser, let's ${args["action"] === "navigate" ? "see what's on that page" : args["action"] === "screenshot" ? "get a visual" : `${args["action"] ?? "do this"}`}`;
20337
20600
  default:
20338
20601
  return `Pulling in ${toolName}, hang tight`;
20339
20602
  }
@@ -20589,7 +20852,7 @@ var init_voice = __esm({
20589
20852
  const audioData = result["output"].data;
20590
20853
  if (audioData.length === 0)
20591
20854
  return;
20592
- const wavPath = join29(tmpdir6(), `oa-voice-${Date.now()}.wav`);
20855
+ const wavPath = join30(tmpdir6(), `oa-voice-${Date.now()}.wav`);
20593
20856
  this.writeWav(audioData, this.config.audio.sample_rate, wavPath);
20594
20857
  await this.playWav(wavPath);
20595
20858
  try {
@@ -20719,7 +20982,7 @@ var init_voice = __esm({
20719
20982
  }
20720
20983
  for (const player of ["paplay", "pw-play", "aplay"]) {
20721
20984
  try {
20722
- execSync18(`which ${player}`, { stdio: "pipe" });
20985
+ execSync19(`which ${player}`, { stdio: "pipe" });
20723
20986
  return [player, path];
20724
20987
  } catch {
20725
20988
  }
@@ -20744,14 +21007,14 @@ var init_voice = __esm({
20744
21007
  const arch = process.arch;
20745
21008
  const isArmLinux = (arch === "arm64" || arch === "arm") && process.platform === "linux";
20746
21009
  mkdirSync9(voiceDir(), { recursive: true });
20747
- const pkgPath = join29(voiceDir(), "package.json");
21010
+ const pkgPath = join30(voiceDir(), "package.json");
20748
21011
  const expectedDeps = {
20749
21012
  "onnxruntime-node": "^1.21.0",
20750
21013
  "phonemizer": "^1.2.1"
20751
21014
  };
20752
- if (existsSync23(pkgPath)) {
21015
+ if (existsSync24(pkgPath)) {
20753
21016
  try {
20754
- const existing = JSON.parse(readFileSync16(pkgPath, "utf8"));
21017
+ const existing = JSON.parse(readFileSync17(pkgPath, "utf8"));
20755
21018
  if (!existing.dependencies?.["phonemizer"]) {
20756
21019
  existing.dependencies = { ...existing.dependencies, ...expectedDeps };
20757
21020
  writeFileSync9(pkgPath, JSON.stringify(existing, null, 2));
@@ -20759,14 +21022,14 @@ var init_voice = __esm({
20759
21022
  } catch {
20760
21023
  }
20761
21024
  }
20762
- if (!existsSync23(pkgPath)) {
21025
+ if (!existsSync24(pkgPath)) {
20763
21026
  writeFileSync9(pkgPath, JSON.stringify({
20764
21027
  name: "open-agents-voice",
20765
21028
  private: true,
20766
21029
  dependencies: expectedDeps
20767
21030
  }, null, 2));
20768
21031
  }
20769
- const voiceRequire = createRequire(join29(voiceDir(), "index.js"));
21032
+ const voiceRequire = createRequire(join30(voiceDir(), "index.js"));
20770
21033
  try {
20771
21034
  this.ort = voiceRequire("onnxruntime-node");
20772
21035
  } catch {
@@ -20775,7 +21038,7 @@ var init_voice = __esm({
20775
21038
  }
20776
21039
  renderInfo("Installing ONNX runtime for voice synthesis...");
20777
21040
  try {
20778
- execSync18("npm install --no-audit --no-fund", {
21041
+ execSync19("npm install --no-audit --no-fund", {
20779
21042
  cwd: voiceDir(),
20780
21043
  stdio: "pipe",
20781
21044
  timeout: 12e4
@@ -20796,7 +21059,7 @@ Error: ${err instanceof Error ? err.message : String(err)}`);
20796
21059
  }
20797
21060
  renderInfo("Installing phonemizer for voice synthesis...");
20798
21061
  try {
20799
- execSync18("npm install --no-audit --no-fund", {
21062
+ execSync19("npm install --no-audit --no-fund", {
20800
21063
  cwd: voiceDir(),
20801
21064
  stdio: "pipe",
20802
21065
  timeout: 12e4
@@ -20820,10 +21083,10 @@ Error: ${err instanceof Error ? err.message : String(err)}`);
20820
21083
  const dir = modelDir(id);
20821
21084
  const onnxPath = modelOnnxPath(id);
20822
21085
  const configPath = modelConfigPath(id);
20823
- if (existsSync23(onnxPath) && existsSync23(configPath))
21086
+ if (existsSync24(onnxPath) && existsSync24(configPath))
20824
21087
  return;
20825
21088
  mkdirSync9(dir, { recursive: true });
20826
- if (!existsSync23(configPath)) {
21089
+ if (!existsSync24(configPath)) {
20827
21090
  renderInfo(`Downloading ${model.label} voice config...`);
20828
21091
  const configResp = await fetch(model.configUrl);
20829
21092
  if (!configResp.ok)
@@ -20831,7 +21094,7 @@ Error: ${err instanceof Error ? err.message : String(err)}`);
20831
21094
  const configText = await configResp.text();
20832
21095
  writeFileSync9(configPath, configText);
20833
21096
  }
20834
- if (!existsSync23(onnxPath)) {
21097
+ if (!existsSync24(onnxPath)) {
20835
21098
  renderInfo(`Downloading ${model.label} voice model (this may take a minute)...`);
20836
21099
  const onnxResp = await fetch(model.onnxUrl);
20837
21100
  if (!onnxResp.ok)
@@ -20867,10 +21130,10 @@ Error: ${err instanceof Error ? err.message : String(err)}`);
20867
21130
  throw new Error("ONNX runtime not loaded");
20868
21131
  const onnxPath = modelOnnxPath(this.modelId);
20869
21132
  const configPath = modelConfigPath(this.modelId);
20870
- if (!existsSync23(onnxPath) || !existsSync23(configPath)) {
21133
+ if (!existsSync24(onnxPath) || !existsSync24(configPath)) {
20871
21134
  throw new Error(`Model files not found for ${this.modelId}`);
20872
21135
  }
20873
- this.config = JSON.parse(readFileSync16(configPath, "utf8"));
21136
+ this.config = JSON.parse(readFileSync17(configPath, "utf8"));
20874
21137
  renderInfo("Loading voice model...");
20875
21138
  this.session = await this.ort.InferenceSession.create(onnxPath, {
20876
21139
  executionProviders: ["cpu"],
@@ -21371,10 +21634,10 @@ var init_stream_renderer = __esm({
21371
21634
 
21372
21635
  // packages/cli/dist/tui/edit-history.js
21373
21636
  import { appendFileSync, mkdirSync as mkdirSync10 } from "node:fs";
21374
- import { join as join30 } from "node:path";
21637
+ import { join as join31 } from "node:path";
21375
21638
  function createEditHistoryLogger(repoRoot, sessionId) {
21376
- const historyDir = join30(repoRoot, ".oa", "history");
21377
- const logPath = join30(historyDir, "edits.jsonl");
21639
+ const historyDir = join31(repoRoot, ".oa", "history");
21640
+ const logPath = join31(historyDir, "edits.jsonl");
21378
21641
  try {
21379
21642
  mkdirSync10(historyDir, { recursive: true });
21380
21643
  } catch {
@@ -21485,9 +21748,9 @@ var init_edit_history = __esm({
21485
21748
  });
21486
21749
 
21487
21750
  // packages/cli/dist/tui/dream-engine.js
21488
- import { mkdirSync as mkdirSync11, writeFileSync as writeFileSync10, readFileSync as readFileSync17, existsSync as existsSync24, cpSync, rmSync, readdirSync as readdirSync10 } from "node:fs";
21489
- import { join as join31, basename as basename12 } from "node:path";
21490
- import { execSync as execSync19 } from "node:child_process";
21751
+ import { mkdirSync as mkdirSync11, writeFileSync as writeFileSync10, readFileSync as readFileSync18, existsSync as existsSync25, cpSync, rmSync, readdirSync as readdirSync10 } from "node:fs";
21752
+ import { join as join32, basename as basename12 } from "node:path";
21753
+ import { execSync as execSync20 } from "node:child_process";
21491
21754
  function adaptTool(tool) {
21492
21755
  return {
21493
21756
  name: tool.name,
@@ -21661,12 +21924,12 @@ var init_dream_engine = __esm({
21661
21924
  const content = String(args["content"] ?? "");
21662
21925
  if (!rawPath)
21663
21926
  return { success: false, output: "", error: "path is required", durationMs: Date.now() - start };
21664
- const targetPath = rawPath.startsWith("/") || rawPath.startsWith(".oa/dreams") ? join31(this.dreamsDir, basename12(rawPath)) : join31(this.dreamsDir, rawPath);
21927
+ const targetPath = rawPath.startsWith("/") || rawPath.startsWith(".oa/dreams") ? join32(this.dreamsDir, basename12(rawPath)) : join32(this.dreamsDir, rawPath);
21665
21928
  if (!targetPath.startsWith(this.dreamsDir)) {
21666
21929
  return { success: false, output: "", error: "Dream mode: writes are confined to .oa/dreams/", durationMs: Date.now() - start };
21667
21930
  }
21668
21931
  try {
21669
- const dir = join31(targetPath, "..");
21932
+ const dir = join32(targetPath, "..");
21670
21933
  mkdirSync11(dir, { recursive: true });
21671
21934
  writeFileSync10(targetPath, content, "utf-8");
21672
21935
  return { success: true, output: `Wrote ${content.length} bytes to ${rawPath}`, durationMs: Date.now() - start };
@@ -21696,15 +21959,15 @@ var init_dream_engine = __esm({
21696
21959
  const rawPath = String(args["path"] ?? "");
21697
21960
  const oldStr = String(args["old_string"] ?? "");
21698
21961
  const newStr = String(args["new_string"] ?? "");
21699
- const targetPath = rawPath.startsWith("/") || rawPath.startsWith(".oa/dreams") ? join31(this.dreamsDir, basename12(rawPath)) : join31(this.dreamsDir, rawPath);
21962
+ const targetPath = rawPath.startsWith("/") || rawPath.startsWith(".oa/dreams") ? join32(this.dreamsDir, basename12(rawPath)) : join32(this.dreamsDir, rawPath);
21700
21963
  if (!targetPath.startsWith(this.dreamsDir)) {
21701
21964
  return { success: false, output: "", error: "Dream mode: edits are confined to .oa/dreams/", durationMs: Date.now() - start };
21702
21965
  }
21703
21966
  try {
21704
- if (!existsSync24(targetPath)) {
21967
+ if (!existsSync25(targetPath)) {
21705
21968
  return { success: false, output: "", error: `File not found: ${rawPath}`, durationMs: Date.now() - start };
21706
21969
  }
21707
- let content = readFileSync17(targetPath, "utf-8");
21970
+ let content = readFileSync18(targetPath, "utf-8");
21708
21971
  if (!content.includes(oldStr)) {
21709
21972
  return { success: false, output: "", error: "old_string not found in file", durationMs: Date.now() - start };
21710
21973
  }
@@ -21740,7 +22003,7 @@ var init_dream_engine = __esm({
21740
22003
  }
21741
22004
  }
21742
22005
  try {
21743
- const output = execSync19(cmd, {
22006
+ const output = execSync20(cmd, {
21744
22007
  cwd: this.repoRoot,
21745
22008
  timeout: 3e4,
21746
22009
  encoding: "utf-8",
@@ -21763,7 +22026,7 @@ var init_dream_engine = __esm({
21763
22026
  constructor(config, repoRoot) {
21764
22027
  this.config = config;
21765
22028
  this.repoRoot = repoRoot;
21766
- this.dreamsDir = join31(repoRoot, ".oa", "dreams");
22029
+ this.dreamsDir = join32(repoRoot, ".oa", "dreams");
21767
22030
  this.state = {
21768
22031
  mode: "default",
21769
22032
  active: false,
@@ -21835,7 +22098,7 @@ ${result.summary}`;
21835
22098
  if (mode !== "default" || cycle === totalCycles) {
21836
22099
  renderDreamContraction(cycle);
21837
22100
  const cycleSummary = this.buildCycleSummary(cycle, previousFindings);
21838
- const summaryPath = join31(this.dreamsDir, `cycle-${cycle}-summary.md`);
22101
+ const summaryPath = join32(this.dreamsDir, `cycle-${cycle}-summary.md`);
21839
22102
  writeFileSync10(summaryPath, cycleSummary, "utf-8");
21840
22103
  }
21841
22104
  if (mode === "lucid" && !this.abortController.signal.aborted) {
@@ -21958,29 +22221,29 @@ Dreams directory: ${this.dreamsDir}`);
21958
22221
  }
21959
22222
  /** Save workspace backup for lucid mode */
21960
22223
  saveVersionCheckpoint(cycle) {
21961
- const checkpointDir = join31(this.dreamsDir, "checkpoints", `cycle-${cycle}`);
22224
+ const checkpointDir = join32(this.dreamsDir, "checkpoints", `cycle-${cycle}`);
21962
22225
  try {
21963
22226
  mkdirSync11(checkpointDir, { recursive: true });
21964
22227
  try {
21965
- const gitStatus = execSync19("git status --porcelain", {
22228
+ const gitStatus = execSync20("git status --porcelain", {
21966
22229
  cwd: this.repoRoot,
21967
22230
  encoding: "utf-8",
21968
22231
  timeout: 1e4
21969
22232
  });
21970
- const gitDiff = execSync19("git diff", {
22233
+ const gitDiff = execSync20("git diff", {
21971
22234
  cwd: this.repoRoot,
21972
22235
  encoding: "utf-8",
21973
22236
  timeout: 1e4
21974
22237
  });
21975
- const gitHash = execSync19("git rev-parse HEAD 2>/dev/null || echo 'no-git'", {
22238
+ const gitHash = execSync20("git rev-parse HEAD 2>/dev/null || echo 'no-git'", {
21976
22239
  cwd: this.repoRoot,
21977
22240
  encoding: "utf-8",
21978
22241
  timeout: 5e3
21979
22242
  }).trim();
21980
- writeFileSync10(join31(checkpointDir, "git-status.txt"), gitStatus, "utf-8");
21981
- writeFileSync10(join31(checkpointDir, "git-diff.patch"), gitDiff, "utf-8");
21982
- writeFileSync10(join31(checkpointDir, "git-hash.txt"), gitHash, "utf-8");
21983
- writeFileSync10(join31(checkpointDir, "checkpoint.json"), JSON.stringify({
22243
+ writeFileSync10(join32(checkpointDir, "git-status.txt"), gitStatus, "utf-8");
22244
+ writeFileSync10(join32(checkpointDir, "git-diff.patch"), gitDiff, "utf-8");
22245
+ writeFileSync10(join32(checkpointDir, "git-hash.txt"), gitHash, "utf-8");
22246
+ writeFileSync10(join32(checkpointDir, "checkpoint.json"), JSON.stringify({
21984
22247
  cycle,
21985
22248
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
21986
22249
  gitHash,
@@ -21988,7 +22251,7 @@ Dreams directory: ${this.dreamsDir}`);
21988
22251
  }, null, 2), "utf-8");
21989
22252
  renderInfo(`Checkpoint saved: cycle ${cycle} (${gitHash.slice(0, 8)})`);
21990
22253
  } catch {
21991
- writeFileSync10(join31(checkpointDir, "checkpoint.json"), JSON.stringify({ cycle, timestamp: (/* @__PURE__ */ new Date()).toISOString(), mode: this.state.mode }, null, 2), "utf-8");
22254
+ writeFileSync10(join32(checkpointDir, "checkpoint.json"), JSON.stringify({ cycle, timestamp: (/* @__PURE__ */ new Date()).toISOString(), mode: this.state.mode }, null, 2), "utf-8");
21992
22255
  renderInfo(`Checkpoint saved: cycle ${cycle} (no git)`);
21993
22256
  }
21994
22257
  } catch (err) {
@@ -22046,14 +22309,14 @@ ${files.map((f) => `- [\`${f}\`](./${f})`).join("\n")}
22046
22309
  ---
22047
22310
  *Auto-generated by open-agents dream engine*
22048
22311
  `;
22049
- writeFileSync10(join31(this.dreamsDir, "PROPOSAL-INDEX.md"), index, "utf-8");
22312
+ writeFileSync10(join32(this.dreamsDir, "PROPOSAL-INDEX.md"), index, "utf-8");
22050
22313
  } catch {
22051
22314
  }
22052
22315
  }
22053
22316
  /** Save dream state for resume/inspection */
22054
22317
  saveDreamState() {
22055
22318
  try {
22056
- writeFileSync10(join31(this.dreamsDir, "dream-state.json"), JSON.stringify(this.state, null, 2) + "\n", "utf-8");
22319
+ writeFileSync10(join32(this.dreamsDir, "dream-state.json"), JSON.stringify(this.state, null, 2) + "\n", "utf-8");
22057
22320
  } catch {
22058
22321
  }
22059
22322
  }
@@ -23109,11 +23372,11 @@ var init_status_bar = __esm({
23109
23372
  import * as readline2 from "node:readline";
23110
23373
  import { Writable } from "node:stream";
23111
23374
  import { cwd } from "node:process";
23112
- import { resolve as resolve20, join as join32, dirname as dirname10, extname as extname9 } from "node:path";
23375
+ import { resolve as resolve20, join as join33, dirname as dirname11, extname as extname9 } from "node:path";
23113
23376
  import { createRequire as createRequire2 } from "node:module";
23114
- import { fileURLToPath as fileURLToPath7 } from "node:url";
23115
- import { readFileSync as readFileSync18, rmSync as rmSync2 } from "node:fs";
23116
- import { existsSync as existsSync25 } from "node:fs";
23377
+ import { fileURLToPath as fileURLToPath8 } from "node:url";
23378
+ import { readFileSync as readFileSync19, rmSync as rmSync2 } from "node:fs";
23379
+ import { existsSync as existsSync26 } from "node:fs";
23117
23380
  function formatTimeAgo(date) {
23118
23381
  const seconds = Math.floor((Date.now() - date.getTime()) / 1e3);
23119
23382
  if (seconds < 60)
@@ -23130,14 +23393,14 @@ function formatTimeAgo(date) {
23130
23393
  function getVersion() {
23131
23394
  try {
23132
23395
  const require2 = createRequire2(import.meta.url);
23133
- const thisDir = dirname10(fileURLToPath7(import.meta.url));
23396
+ const thisDir = dirname11(fileURLToPath8(import.meta.url));
23134
23397
  const candidates = [
23135
- join32(thisDir, "..", "package.json"),
23136
- join32(thisDir, "..", "..", "package.json"),
23137
- join32(thisDir, "..", "..", "..", "package.json")
23398
+ join33(thisDir, "..", "package.json"),
23399
+ join33(thisDir, "..", "..", "package.json"),
23400
+ join33(thisDir, "..", "..", "..", "package.json")
23138
23401
  ];
23139
23402
  for (const pkgPath of candidates) {
23140
- if (existsSync25(pkgPath)) {
23403
+ if (existsSync26(pkgPath)) {
23141
23404
  const pkg = require2(pkgPath);
23142
23405
  if (pkg.name === "open-agents-ai" || pkg.name === "@open-agents/cli") {
23143
23406
  return pkg.version ?? "0.0.0";
@@ -23235,7 +23498,9 @@ function buildTools(repoRoot, config, contextWindowSize) {
23235
23498
  new OcrPdfTool(repoRoot),
23236
23499
  new PdfToTextTool(repoRoot),
23237
23500
  // Advanced image OCR (multi-variant preprocessing pipeline)
23238
- new OcrImageAdvancedTool(repoRoot)
23501
+ new OcrImageAdvancedTool(repoRoot),
23502
+ // Browser automation (headless Chrome via Hydra scrape service)
23503
+ new BrowserActionTool()
23239
23504
  ];
23240
23505
  return [
23241
23506
  ...executionTools.map(adaptTool2),
@@ -24230,8 +24495,8 @@ async function startInteractive(config, repoPath) {
24230
24495
  return true;
24231
24496
  },
24232
24497
  destroyProject() {
24233
- const oaPath = join32(repoRoot, OA_DIR);
24234
- if (existsSync25(oaPath)) {
24498
+ const oaPath = join33(repoRoot, OA_DIR);
24499
+ if (existsSync26(oaPath)) {
24235
24500
  try {
24236
24501
  rmSync2(oaPath, { recursive: true, force: true });
24237
24502
  writeContent(() => renderInfo(`Removed ${OA_DIR}/ directory.`));
@@ -24463,13 +24728,13 @@ Execute this skill now. Follow the behavioral guidance above.`;
24463
24728
  }
24464
24729
  }
24465
24730
  const cleanPath = input.replace(/^['"]|['"]$/g, "").trim();
24466
- const isImage = isImagePath(cleanPath) && existsSync25(resolve20(repoRoot, cleanPath));
24467
- const isMedia = !isImage && isTranscribablePath(cleanPath) && existsSync25(resolve20(repoRoot, cleanPath));
24731
+ const isImage = isImagePath(cleanPath) && existsSync26(resolve20(repoRoot, cleanPath));
24732
+ const isMedia = !isImage && isTranscribablePath(cleanPath) && existsSync26(resolve20(repoRoot, cleanPath));
24468
24733
  if (activeTask) {
24469
24734
  if (isImage) {
24470
24735
  try {
24471
24736
  const imgPath = resolve20(repoRoot, cleanPath);
24472
- const imgBuffer = readFileSync18(imgPath);
24737
+ const imgBuffer = readFileSync19(imgPath);
24473
24738
  const base64 = imgBuffer.toString("base64");
24474
24739
  const ext = extname9(cleanPath).toLowerCase();
24475
24740
  const mime = ext === ".png" ? "image/png" : ext === ".gif" ? "image/gif" : ext === ".webp" ? "image/webp" : "image/jpeg";
@@ -24752,7 +25017,7 @@ import { glob } from "glob";
24752
25017
  import ignore from "ignore";
24753
25018
  import { readFile as readFile11, stat as stat4 } from "node:fs/promises";
24754
25019
  import { createHash } from "node:crypto";
24755
- import { join as join33, relative as relative3, extname as extname10, basename as basename13 } from "node:path";
25020
+ import { join as join34, relative as relative3, extname as extname10, basename as basename13 } from "node:path";
24756
25021
  var DEFAULT_EXCLUDE, LANGUAGE_MAP, CodebaseIndexer;
24757
25022
  var init_codebase_indexer = __esm({
24758
25023
  "packages/indexer/dist/codebase-indexer.js"() {
@@ -24796,7 +25061,7 @@ var init_codebase_indexer = __esm({
24796
25061
  const ig = ignore.default();
24797
25062
  if (this.config.respectGitignore) {
24798
25063
  try {
24799
- const gitignoreContent = await readFile11(join33(this.config.rootDir, ".gitignore"), "utf-8");
25064
+ const gitignoreContent = await readFile11(join34(this.config.rootDir, ".gitignore"), "utf-8");
24800
25065
  ig.add(gitignoreContent);
24801
25066
  } catch {
24802
25067
  }
@@ -24811,7 +25076,7 @@ var init_codebase_indexer = __esm({
24811
25076
  for (const relativePath of files) {
24812
25077
  if (ig.ignores(relativePath))
24813
25078
  continue;
24814
- const fullPath = join33(this.config.rootDir, relativePath);
25079
+ const fullPath = join34(this.config.rootDir, relativePath);
24815
25080
  try {
24816
25081
  const fileStat = await stat4(fullPath);
24817
25082
  if (fileStat.size > this.config.maxFileSize)
@@ -24857,7 +25122,7 @@ var init_codebase_indexer = __esm({
24857
25122
  if (!child) {
24858
25123
  child = {
24859
25124
  name: part,
24860
- path: join33(current.path, part),
25125
+ path: join34(current.path, part),
24861
25126
  type: "directory",
24862
25127
  children: []
24863
25128
  };
@@ -24940,13 +25205,13 @@ __export(index_repo_exports, {
24940
25205
  indexRepoCommand: () => indexRepoCommand
24941
25206
  });
24942
25207
  import { resolve as resolve21 } from "node:path";
24943
- import { existsSync as existsSync26, statSync as statSync10 } from "node:fs";
25208
+ import { existsSync as existsSync27, statSync as statSync10 } from "node:fs";
24944
25209
  import { cwd as cwd2 } from "node:process";
24945
25210
  async function indexRepoCommand(opts, _config) {
24946
25211
  const repoRoot = resolve21(opts.repoPath ?? cwd2());
24947
25212
  printHeader("Index Repository");
24948
25213
  printInfo(`Indexing: ${repoRoot}`);
24949
- if (!existsSync26(repoRoot)) {
25214
+ if (!existsSync27(repoRoot)) {
24950
25215
  printError(`Path does not exist: ${repoRoot}`);
24951
25216
  process.exit(1);
24952
25217
  }
@@ -25198,7 +25463,7 @@ var config_exports = {};
25198
25463
  __export(config_exports, {
25199
25464
  configCommand: () => configCommand
25200
25465
  });
25201
- import { join as join34, resolve as resolve22 } from "node:path";
25466
+ import { join as join35, resolve as resolve22 } from "node:path";
25202
25467
  import { homedir as homedir13 } from "node:os";
25203
25468
  import { cwd as cwd3 } from "node:process";
25204
25469
  function redactIfSensitive(key, value) {
@@ -25257,7 +25522,7 @@ function handleShow(opts, config) {
25257
25522
  }
25258
25523
  }
25259
25524
  printSection("Config File");
25260
- printInfo(`~/.open-agents/config.json (${join34(homedir13(), ".open-agents", "config.json")})`);
25525
+ printInfo(`~/.open-agents/config.json (${join35(homedir13(), ".open-agents", "config.json")})`);
25261
25526
  printSection("Priority Chain");
25262
25527
  printInfo(" 1. CLI flags (--model, --backend-url, etc.)");
25263
25528
  printInfo(" 2. Project .oa/settings.json (--local)");
@@ -25296,7 +25561,7 @@ function handleSet(opts, _config) {
25296
25561
  const coerced = coerceForSettings(key, value);
25297
25562
  saveProjectSettings(repoRoot, { [key]: coerced });
25298
25563
  printSuccess(`Project override set: ${key} = ${redactIfSensitive(key, value)}`);
25299
- printInfo(`Saved to ${join34(repoRoot, ".oa", "settings.json")}`);
25564
+ printInfo(`Saved to ${join35(repoRoot, ".oa", "settings.json")}`);
25300
25565
  printInfo("This override applies only when running in this workspace.");
25301
25566
  } catch (err) {
25302
25567
  printError(`Failed to save: ${err instanceof Error ? err.message : String(err)}`);
@@ -25357,7 +25622,7 @@ var serve_exports = {};
25357
25622
  __export(serve_exports, {
25358
25623
  serveCommand: () => serveCommand
25359
25624
  });
25360
- import { spawn as spawn9 } from "node:child_process";
25625
+ import { spawn as spawn10 } from "node:child_process";
25361
25626
  async function serveCommand(opts, config) {
25362
25627
  const backendType = config.backendType;
25363
25628
  if (backendType === "ollama") {
@@ -25450,7 +25715,7 @@ async function serveVllm(opts, config) {
25450
25715
  }
25451
25716
  async function runVllmServer(args, verbose) {
25452
25717
  return new Promise((resolve23, reject) => {
25453
- const child = spawn9("python", args, {
25718
+ const child = spawn10("python", args, {
25454
25719
  stdio: verbose ? "inherit" : ["ignore", "pipe", "pipe"],
25455
25720
  env: { ...process.env }
25456
25721
  });
@@ -25516,7 +25781,7 @@ __export(eval_exports, {
25516
25781
  });
25517
25782
  import { tmpdir as tmpdir7 } from "node:os";
25518
25783
  import { mkdirSync as mkdirSync12, writeFileSync as writeFileSync11 } from "node:fs";
25519
- import { join as join35 } from "node:path";
25784
+ import { join as join36 } from "node:path";
25520
25785
  async function evalCommand(opts, config) {
25521
25786
  const suiteName = opts.suite ?? "basic";
25522
25787
  const suite = SUITES[suiteName];
@@ -25637,9 +25902,9 @@ async function evalCommand(opts, config) {
25637
25902
  process.exit(failed > 0 ? 1 : 0);
25638
25903
  }
25639
25904
  function createTempEvalRepo() {
25640
- const dir = join35(tmpdir7(), `open-agents-eval-${Date.now()}`);
25905
+ const dir = join36(tmpdir7(), `open-agents-eval-${Date.now()}`);
25641
25906
  mkdirSync12(dir, { recursive: true });
25642
- writeFileSync11(join35(dir, "package.json"), JSON.stringify({ name: "eval-repo", version: "0.0.0" }, null, 2) + "\n", "utf8");
25907
+ writeFileSync11(join36(dir, "package.json"), JSON.stringify({ name: "eval-repo", version: "0.0.0" }, null, 2) + "\n", "utf8");
25643
25908
  return dir;
25644
25909
  }
25645
25910
  var BASIC_SUITE, FULL_SUITE, SUITES;
@@ -25698,8 +25963,8 @@ init_output();
25698
25963
  init_updater();
25699
25964
  import { parseArgs as nodeParseArgs2 } from "node:util";
25700
25965
  import { createRequire as createRequire3 } from "node:module";
25701
- import { fileURLToPath as fileURLToPath8 } from "node:url";
25702
- import { dirname as dirname11, join as join36 } from "node:path";
25966
+ import { fileURLToPath as fileURLToPath9 } from "node:url";
25967
+ import { dirname as dirname12, join as join37 } from "node:path";
25703
25968
 
25704
25969
  // packages/cli/dist/cli.js
25705
25970
  import { createInterface } from "node:readline";
@@ -25806,7 +26071,7 @@ init_output();
25806
26071
  function getVersion2() {
25807
26072
  try {
25808
26073
  const require2 = createRequire3(import.meta.url);
25809
- const pkgPath = join36(dirname11(fileURLToPath8(import.meta.url)), "..", "package.json");
26074
+ const pkgPath = join37(dirname12(fileURLToPath9(import.meta.url)), "..", "package.json");
25810
26075
  const pkg = require2(pkgPath);
25811
26076
  return pkg.version;
25812
26077
  } catch {