open-agents-ai 0.4.0 → 0.5.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.
Files changed (3) hide show
  1. package/README.md +22 -0
  2. package/dist/index.js +658 -85
  3. package/package.json +5 -2
package/README.md CHANGED
@@ -160,6 +160,28 @@ The agent follows an iterative fix loop:
160
160
  -V, --version Show version
161
161
  ```
162
162
 
163
+ ### Voice Feedback (TTS)
164
+
165
+ The agent can speak what it's doing using neural TTS voices. Enable it in the interactive REPL:
166
+
167
+ ```bash
168
+ /voice # Toggle voice on/off (default: GLaDOS)
169
+ /voice glados # Switch to GLaDOS voice
170
+ /voice overwatch # Switch to Overwatch voice
171
+ ```
172
+
173
+ On first enable, the agent auto-downloads the ONNX voice model (~50MB) and installs `onnxruntime-node` in `~/.open-agents/voice/`. For best quality, install `espeak-ng`:
174
+
175
+ ```bash
176
+ # Ubuntu/Debian
177
+ sudo apt install espeak-ng
178
+
179
+ # macOS
180
+ brew install espeak-ng
181
+ ```
182
+
183
+ When enabled, the agent speaks brief descriptions of each tool call ("Reading auth.ts", "Running tests", "Editing config.js") through your system speakers.
184
+
163
185
  ### Configuration
164
186
 
165
187
  Config priority: CLI flags > environment variables > `~/.open-agents/config.json` > defaults.
package/dist/index.js CHANGED
@@ -1116,21 +1116,29 @@ var init_tool_executor = __esm({
1116
1116
  });
1117
1117
 
1118
1118
  // packages/execution/dist/tools/shell.js
1119
- import { execFile } from "node:child_process";
1120
- import { promisify } from "node:util";
1121
- var execFileAsync, ShellTool;
1119
+ import { spawn } from "node:child_process";
1120
+ var ShellTool;
1122
1121
  var init_shell = __esm({
1123
1122
  "packages/execution/dist/tools/shell.js"() {
1124
1123
  "use strict";
1125
- execFileAsync = promisify(execFile);
1126
1124
  ShellTool = class {
1127
1125
  name = "shell";
1128
- description = "Execute a shell command in the project working directory";
1126
+ description = "Execute a shell command in the project working directory. Commands run non-interactively (CI=true). For commands that need input, use the stdin parameter or pass non-interactive flags (--yes, --no-input).";
1129
1127
  parameters = {
1130
1128
  type: "object",
1131
1129
  properties: {
1132
- command: { type: "string", description: "The shell command to execute" },
1133
- timeout: { type: "number", description: "Timeout in milliseconds" }
1130
+ command: {
1131
+ type: "string",
1132
+ description: "The shell command to execute"
1133
+ },
1134
+ timeout: {
1135
+ type: "number",
1136
+ description: "Timeout in milliseconds (default: 30000)"
1137
+ },
1138
+ stdin: {
1139
+ type: "string",
1140
+ description: "Text to write to the command's standard input. Use newlines to separate multiple inputs. For arrow-key prompts, prefer --yes or non-interactive flags instead."
1141
+ }
1134
1142
  },
1135
1143
  required: ["command"]
1136
1144
  };
@@ -1143,29 +1151,89 @@ var init_shell = __esm({
1143
1151
  async execute(args) {
1144
1152
  const command = args["command"];
1145
1153
  const timeout = args["timeout"] ?? this.defaultTimeout;
1154
+ const stdinInput = args["stdin"];
1146
1155
  const start = performance.now();
1147
- try {
1148
- const { stdout, stderr } = await execFileAsync("bash", ["-c", command], {
1156
+ return new Promise((resolve12) => {
1157
+ const child = spawn("bash", ["-c", command], {
1149
1158
  cwd: this.workingDir,
1150
- timeout,
1151
- maxBuffer: 1024 * 1024
1159
+ env: {
1160
+ ...process.env,
1161
+ // Non-interactive mode: libraries like prompts, inquirer,
1162
+ // create-next-app, and many Node CLI tools check CI to skip
1163
+ // interactive prompts and use defaults instead.
1164
+ CI: "true",
1165
+ NONINTERACTIVE: "1",
1166
+ // Disable color in most tools to get clean output
1167
+ NO_COLOR: "1",
1168
+ FORCE_COLOR: "0"
1169
+ },
1170
+ stdio: ["pipe", "pipe", "pipe"]
1152
1171
  });
1153
- return {
1154
- success: true,
1155
- output: stdout + (stderr ? `
1172
+ let stdout = "";
1173
+ let stderr = "";
1174
+ let killed = false;
1175
+ const maxBuf = 1024 * 1024;
1176
+ const timer = setTimeout(() => {
1177
+ killed = true;
1178
+ child.kill("SIGTERM");
1179
+ setTimeout(() => {
1180
+ try {
1181
+ child.kill("SIGKILL");
1182
+ } catch {
1183
+ }
1184
+ }, 5e3);
1185
+ }, timeout);
1186
+ child.stdout.on("data", (data) => {
1187
+ stdout += data.toString();
1188
+ if (stdout.length > maxBuf) {
1189
+ stdout = stdout.slice(0, maxBuf);
1190
+ }
1191
+ });
1192
+ child.stderr.on("data", (data) => {
1193
+ stderr += data.toString();
1194
+ if (stderr.length > maxBuf) {
1195
+ stderr = stderr.slice(0, maxBuf);
1196
+ }
1197
+ });
1198
+ if (stdinInput) {
1199
+ child.stdin.write(stdinInput);
1200
+ }
1201
+ child.stdin.end();
1202
+ child.on("close", (code) => {
1203
+ clearTimeout(timer);
1204
+ const durationMs = performance.now() - start;
1205
+ if (killed) {
1206
+ const combined = stdout + stderr;
1207
+ const looksInteractive = /\? .+[›>]|y\/n|yes\/no|\(Y\/n\)|\[y\/N\]/i.test(combined);
1208
+ const hint = looksInteractive ? " The command appears to be waiting for interactive input. Use non-interactive flags (e.g., --yes, --no-input) or provide input via the stdin parameter." : "";
1209
+ resolve12({
1210
+ success: false,
1211
+ output: stdout,
1212
+ error: `Command timed out after ${timeout}ms.${hint}`,
1213
+ durationMs
1214
+ });
1215
+ return;
1216
+ }
1217
+ const success = code === 0;
1218
+ resolve12({
1219
+ success,
1220
+ output: stdout + (stderr && success ? `
1156
1221
  STDERR:
1157
1222
  ${stderr}` : ""),
1158
- durationMs: performance.now() - start
1159
- };
1160
- } catch (error) {
1161
- const err = error;
1162
- return {
1163
- success: false,
1164
- output: err.stdout ?? "",
1165
- error: err.stderr ?? err.message,
1166
- durationMs: performance.now() - start
1167
- };
1168
- }
1223
+ error: success ? void 0 : stderr || `Exit code ${code}`,
1224
+ durationMs
1225
+ });
1226
+ });
1227
+ child.on("error", (err) => {
1228
+ clearTimeout(timer);
1229
+ resolve12({
1230
+ success: false,
1231
+ output: stdout,
1232
+ error: err.message,
1233
+ durationMs: performance.now() - start
1234
+ });
1235
+ });
1236
+ });
1169
1237
  }
1170
1238
  };
1171
1239
  }
@@ -1275,14 +1343,14 @@ var init_file_write = __esm({
1275
1343
  });
1276
1344
 
1277
1345
  // packages/execution/dist/tools/grep-search.js
1278
- import { execFile as execFile2 } from "node:child_process";
1279
- import { promisify as promisify2 } from "node:util";
1346
+ import { execFile } from "node:child_process";
1347
+ import { promisify } from "node:util";
1280
1348
  import { resolve as resolve3 } from "node:path";
1281
- var execFileAsync2, MAX_OUTPUT_LINES, GrepSearchTool;
1349
+ var execFileAsync, MAX_OUTPUT_LINES, GrepSearchTool;
1282
1350
  var init_grep_search = __esm({
1283
1351
  "packages/execution/dist/tools/grep-search.js"() {
1284
1352
  "use strict";
1285
- execFileAsync2 = promisify2(execFile2);
1353
+ execFileAsync = promisify(execFile);
1286
1354
  MAX_OUTPUT_LINES = 100;
1287
1355
  GrepSearchTool = class {
1288
1356
  name = "grep_search";
@@ -1323,7 +1391,7 @@ var init_grep_search = __esm({
1323
1391
  rgArgs.push("--glob", include);
1324
1392
  }
1325
1393
  rgArgs.push(pattern, searchPath);
1326
- const { stdout } = await execFileAsync2("rg", rgArgs, {
1394
+ const { stdout } = await execFileAsync("rg", rgArgs, {
1327
1395
  cwd: this.workingDir,
1328
1396
  maxBuffer: 1024 * 1024
1329
1397
  });
@@ -1334,7 +1402,7 @@ var init_grep_search = __esm({
1334
1402
  grepArgs.push(`--include=${include}`);
1335
1403
  }
1336
1404
  grepArgs.push(pattern, searchPath);
1337
- const { stdout } = await execFileAsync2("grep", grepArgs, {
1405
+ const { stdout } = await execFileAsync("grep", grepArgs, {
1338
1406
  cwd: this.workingDir,
1339
1407
  maxBuffer: 1024 * 1024
1340
1408
  });
@@ -1369,7 +1437,7 @@ var init_grep_search = __esm({
1369
1437
  }
1370
1438
  async #isRipgrepAvailable() {
1371
1439
  try {
1372
- await execFileAsync2("rg", ["--version"], { timeout: 2e3 });
1440
+ await execFileAsync("rg", ["--version"], { timeout: 2e3 });
1373
1441
  return true;
1374
1442
  } catch {
1375
1443
  return false;
@@ -1380,14 +1448,14 @@ var init_grep_search = __esm({
1380
1448
  });
1381
1449
 
1382
1450
  // packages/execution/dist/tools/glob-find.js
1383
- import { execFile as execFile3 } from "node:child_process";
1384
- import { promisify as promisify3 } from "node:util";
1451
+ import { execFile as execFile2 } from "node:child_process";
1452
+ import { promisify as promisify2 } from "node:util";
1385
1453
  import { resolve as resolve4 } from "node:path";
1386
- var execFileAsync3, MAX_RESULTS, GlobFindTool;
1454
+ var execFileAsync2, MAX_RESULTS, GlobFindTool;
1387
1455
  var init_glob_find = __esm({
1388
1456
  "packages/execution/dist/tools/glob-find.js"() {
1389
1457
  "use strict";
1390
- execFileAsync3 = promisify3(execFile3);
1458
+ execFileAsync2 = promisify2(execFile2);
1391
1459
  MAX_RESULTS = 50;
1392
1460
  GlobFindTool = class {
1393
1461
  name = "find_files";
@@ -1415,7 +1483,7 @@ var init_glob_find = __esm({
1415
1483
  const searchPath = resolve4(this.workingDir, args["path"] ?? ".");
1416
1484
  const start = performance.now();
1417
1485
  try {
1418
- const { stdout } = await execFileAsync3("find", [
1486
+ const { stdout } = await execFileAsync2("find", [
1419
1487
  searchPath,
1420
1488
  "-type",
1421
1489
  "f",
@@ -4087,13 +4155,13 @@ var init_plannerRunner = __esm({
4087
4155
  });
4088
4156
 
4089
4157
  // packages/retrieval/dist/grep-search.js
4090
- import { execFile as execFile4 } from "node:child_process";
4091
- import { promisify as promisify4 } from "node:util";
4092
- var execFileAsync4, GrepSearch;
4158
+ import { execFile as execFile3 } from "node:child_process";
4159
+ import { promisify as promisify3 } from "node:util";
4160
+ var execFileAsync3, GrepSearch;
4093
4161
  var init_grep_search2 = __esm({
4094
4162
  "packages/retrieval/dist/grep-search.js"() {
4095
4163
  "use strict";
4096
- execFileAsync4 = promisify4(execFile4);
4164
+ execFileAsync3 = promisify3(execFile3);
4097
4165
  GrepSearch = class {
4098
4166
  rootDir;
4099
4167
  constructor(rootDir) {
@@ -4115,7 +4183,7 @@ var init_grep_search2 = __esm({
4115
4183
  }
4116
4184
  args.push(pattern, this.rootDir);
4117
4185
  try {
4118
- const { stdout } = await execFileAsync4("rg", args, {
4186
+ const { stdout } = await execFileAsync3("rg", args, {
4119
4187
  maxBuffer: 10 * 1024 * 1024
4120
4188
  });
4121
4189
  return this.parseRipgrepOutput(stdout);
@@ -4190,8 +4258,8 @@ var init_code_retriever = __esm({
4190
4258
  });
4191
4259
 
4192
4260
  // packages/retrieval/dist/lexicalSearch.js
4193
- import { execFile as execFile5 } from "node:child_process";
4194
- import { promisify as promisify5 } from "node:util";
4261
+ import { execFile as execFile4 } from "node:child_process";
4262
+ import { promisify as promisify4 } from "node:util";
4195
4263
  import { readFile as readFile6, readdir, stat } from "node:fs/promises";
4196
4264
  import { join as join10, extname as extname2 } from "node:path";
4197
4265
  async function searchByPath(pathPattern, options) {
@@ -4235,7 +4303,7 @@ async function runSearch(pattern, kind, options) {
4235
4303
  }
4236
4304
  async function isRipgrepAvailable() {
4237
4305
  try {
4238
- await execFileAsync5("rg", ["--version"], { timeout: 2e3 });
4306
+ await execFileAsync4("rg", ["--version"], { timeout: 2e3 });
4239
4307
  return true;
4240
4308
  } catch {
4241
4309
  return false;
@@ -4255,7 +4323,7 @@ async function searchWithRipgrep(pattern, kind, options) {
4255
4323
  args.push(pattern, options.rootDir);
4256
4324
  let stdout;
4257
4325
  try {
4258
- const result = await execFileAsync5("rg", args, {
4326
+ const result = await execFileAsync4("rg", args, {
4259
4327
  maxBuffer: 20 * 1024 * 1024,
4260
4328
  timeout: 1e4
4261
4329
  });
@@ -4365,11 +4433,11 @@ function matchesGlob(filePath, glob2) {
4365
4433
  return false;
4366
4434
  }
4367
4435
  }
4368
- var execFileAsync5, DEFAULT_INCLUDE_GLOBS, DEFAULT_EXCLUDE_GLOBS, DEFAULT_MAX_MATCHES, ALWAYS_SKIP, ALL_CODE_EXTS;
4436
+ var execFileAsync4, DEFAULT_INCLUDE_GLOBS, DEFAULT_EXCLUDE_GLOBS, DEFAULT_MAX_MATCHES, ALWAYS_SKIP, ALL_CODE_EXTS;
4369
4437
  var init_lexicalSearch = __esm({
4370
4438
  "packages/retrieval/dist/lexicalSearch.js"() {
4371
4439
  "use strict";
4372
- execFileAsync5 = promisify5(execFile5);
4440
+ execFileAsync4 = promisify4(execFile4);
4373
4441
  DEFAULT_INCLUDE_GLOBS = ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.py"];
4374
4442
  DEFAULT_EXCLUDE_GLOBS = ["node_modules", "dist", ".git", "build", "coverage"];
4375
4443
  DEFAULT_MAX_MATCHES = 50;
@@ -5115,9 +5183,9 @@ var init_verifierRunner = __esm({
5115
5183
  async executeTests(patch, repoRoot) {
5116
5184
  if (patch.testsToRun.length === 0)
5117
5185
  return "(no tests specified)";
5118
- const { execFile: execFile6 } = await import("node:child_process");
5119
- const { promisify: promisify6 } = await import("node:util");
5120
- const execFileAsync6 = promisify6(execFile6);
5186
+ const { execFile: execFile5 } = await import("node:child_process");
5187
+ const { promisify: promisify5 } = await import("node:util");
5188
+ const execFileAsync5 = promisify5(execFile5);
5121
5189
  const outputs = [];
5122
5190
  const workDir = this.options.workingDir || repoRoot;
5123
5191
  for (const cmd of patch.testsToRun.slice(0, 3)) {
@@ -5126,7 +5194,7 @@ var init_verifierRunner = __esm({
5126
5194
  const [bin, ...args] = parts;
5127
5195
  if (!bin)
5128
5196
  continue;
5129
- const { stdout, stderr } = await execFileAsync6(bin, args, {
5197
+ const { stdout, stderr } = await execFileAsync5(bin, args, {
5130
5198
  cwd: workDir,
5131
5199
  timeout: 6e4,
5132
5200
  maxBuffer: 1024 * 1024
@@ -5701,7 +5769,7 @@ var init_agenticRunner = __esm({
5701
5769
  - file_edit: Make a precise string replacement in a file (preferred over rewriting). Uses old_string/new_string.
5702
5770
  - find_files: Find files by name pattern (glob). Searches recursively, excludes node_modules/.git.
5703
5771
  - grep_search: Search file contents with regex. Returns matching lines with paths and line numbers.
5704
- - shell: Execute any shell command (tests, builds, git, npm, etc.)
5772
+ - shell: Execute any shell command (tests, builds, git, npm, etc.). Supports stdin parameter for input. Commands run with CI=true for non-interactive mode.
5705
5773
  - list_directory: List files in a directory with types and sizes
5706
5774
  - web_search: Search the web for documentation or solutions
5707
5775
  - web_fetch: Fetch a web page and extract text content (for docs, MDN, w3schools.com, etc.)
@@ -5752,6 +5820,15 @@ When a test or build fails:
5752
5820
  8. If it fails with the SAME error, try a different approach
5753
5821
  9. After 3 failed attempts at the same error, use web_search for solutions
5754
5822
 
5823
+ ## Interactive Commands
5824
+
5825
+ Commands run non-interactively (CI=true). When running scaffolding tools:
5826
+ - ALWAYS add non-interactive flags: --yes, --no-input, --defaults, etc.
5827
+ - For npx create-next-app: use --yes (skips all prompts, uses defaults)
5828
+ - For npm init: use -y
5829
+ - If a command needs specific answers, use the stdin parameter
5830
+ - If a command times out, it likely hit an interactive prompt \u2014 retry with --yes
5831
+
5755
5832
  ## Context Efficiency
5756
5833
 
5757
5834
  - Use grep_search to find specific code instead of reading many files
@@ -6210,6 +6287,8 @@ function renderSlashHelp() {
6210
6287
  ["/endpoint <url> --auth <t>", "Set endpoint with Bearer auth"],
6211
6288
  ["/config", "Show current configuration"],
6212
6289
  ["/update", "Check for updates and auto-install"],
6290
+ ["/voice", "Toggle TTS voice feedback (GLaDOS)"],
6291
+ ["/voice <model>", "Set voice: glados, overwatch"],
6213
6292
  ["/verbose", "Toggle verbose mode"],
6214
6293
  ["/clear", "Clear the screen"],
6215
6294
  ["/help", "Show this help"],
@@ -6267,8 +6346,10 @@ function formatToolArgs(toolName, args) {
6267
6346
  case "file_write":
6268
6347
  case "file_edit":
6269
6348
  return String(args["path"] ?? "");
6270
- case "shell":
6271
- return truncStr(String(args["command"] ?? ""), 60);
6349
+ case "shell": {
6350
+ const cmd = truncStr(String(args["command"] ?? ""), 60);
6351
+ return args["stdin"] ? `${cmd} ${c2.dim("(with stdin)")}` : cmd;
6352
+ }
6272
6353
  case "grep_search":
6273
6354
  return `${c2.yellow(String(args["pattern"] ?? ""))}${args["path"] ? ` in ${args["path"]}` : ""}`;
6274
6355
  case "find_files":
@@ -6440,6 +6521,16 @@ async function handleSlashCommand(input, ctx) {
6440
6521
  case "upgrade":
6441
6522
  await handleUpdate();
6442
6523
  return "handled";
6524
+ case "voice": {
6525
+ if (arg) {
6526
+ const msg = await ctx.voiceSetModel(arg);
6527
+ renderInfo(msg);
6528
+ } else {
6529
+ const msg = await ctx.voiceToggle();
6530
+ renderInfo(msg);
6531
+ }
6532
+ return "handled";
6533
+ }
6443
6534
  default:
6444
6535
  renderWarning(`Unknown command: /${cmd}. Type /help for available commands.`);
6445
6536
  return "handled";
@@ -6552,11 +6643,11 @@ async function handleEndpoint(arg, ctx) {
6552
6643
  async function handleUpdate() {
6553
6644
  let currentVersion = "0.0.0";
6554
6645
  try {
6555
- const { createRequire: createRequire2 } = await import("node:module");
6646
+ const { createRequire: createRequire3 } = await import("node:module");
6556
6647
  const { fileURLToPath: fileURLToPath2 } = await import("node:url");
6557
- const { dirname: dirname4, join: join17 } = await import("node:path");
6558
- const require2 = createRequire2(import.meta.url);
6559
- const pkgPath = join17(dirname4(fileURLToPath2(import.meta.url)), "..", "package.json");
6648
+ const { dirname: dirname4, join: join18 } = await import("node:path");
6649
+ const require2 = createRequire3(import.meta.url);
6650
+ const pkgPath = join18(dirname4(fileURLToPath2(import.meta.url)), "..", "package.json");
6560
6651
  const pkg = require2(pkgPath);
6561
6652
  currentVersion = pkg.version ?? "0.0.0";
6562
6653
  } catch {
@@ -6916,9 +7007,9 @@ async function doSetup(config, rl) {
6916
7007
  `PARAMETER num_predict 16384`,
6917
7008
  `PARAMETER stop "<|endoftext|>"`
6918
7009
  ].join("\n");
6919
- const modelDir = join12(homedir3(), ".open-agents", "models");
6920
- mkdirSync3(modelDir, { recursive: true });
6921
- const modelfilePath = join12(modelDir, `Modelfile.${customName}`);
7010
+ const modelDir2 = join12(homedir3(), ".open-agents", "models");
7011
+ mkdirSync3(modelDir2, { recursive: true });
7012
+ const modelfilePath = join12(modelDir2, `Modelfile.${customName}`);
6922
7013
  writeFileSync3(modelfilePath, modelfileContent + "\n", "utf8");
6923
7014
  process.stdout.write(` ${c2.dim("Creating model...")} `);
6924
7015
  execSync7(`ollama create ${customName} -f ${modelfilePath}`, {
@@ -7003,6 +7094,472 @@ var init_setup = __esm({
7003
7094
  }
7004
7095
  });
7005
7096
 
7097
+ // packages/cli/dist/tui/voice.js
7098
+ import { existsSync as existsSync8, mkdirSync as mkdirSync4, writeFileSync as writeFileSync4, readFileSync as readFileSync6, unlinkSync } from "node:fs";
7099
+ import { join as join13 } from "node:path";
7100
+ import { homedir as homedir4, tmpdir, platform } from "node:os";
7101
+ import { execSync as execSync8, spawn as nodeSpawn } from "node:child_process";
7102
+ import { createRequire } from "node:module";
7103
+ function modelDir(id) {
7104
+ return join13(MODELS_DIR, id);
7105
+ }
7106
+ function modelOnnxPath(id) {
7107
+ return join13(modelDir(id), "model.onnx");
7108
+ }
7109
+ function modelConfigPath(id) {
7110
+ return join13(modelDir(id), "config.json");
7111
+ }
7112
+ function describeToolCall(toolName, args) {
7113
+ const path = args["path"];
7114
+ const file = path ? path.split("/").pop() ?? path : "";
7115
+ switch (toolName) {
7116
+ case "file_read":
7117
+ return `Reading ${file}`;
7118
+ case "file_write":
7119
+ return `Writing ${file}`;
7120
+ case "file_edit":
7121
+ return `Editing ${file}`;
7122
+ case "shell": {
7123
+ const cmd = String(args["command"] ?? "");
7124
+ if (/npm\s+test|vitest|jest|mocha/.test(cmd))
7125
+ return "Running tests";
7126
+ if (/npm\s+run\s+build|tsc|esbuild/.test(cmd))
7127
+ return "Building project";
7128
+ if (/npm\s+install|pnpm\s+install/.test(cmd))
7129
+ return "Installing dependencies";
7130
+ if (/git\s+/.test(cmd))
7131
+ return "Running git command";
7132
+ if (/npm\s+run\s+lint|eslint|biome/.test(cmd))
7133
+ return "Running linter";
7134
+ if (cmd.length > 40)
7135
+ return "Running shell command";
7136
+ return `Running ${cmd.slice(0, 30)}`;
7137
+ }
7138
+ case "grep_search":
7139
+ return `Searching for ${args["pattern"] ?? "pattern"}`;
7140
+ case "find_files":
7141
+ return `Finding files matching ${args["pattern"] ?? "pattern"}`;
7142
+ case "list_directory":
7143
+ return `Listing directory ${file || "contents"}`;
7144
+ case "web_search":
7145
+ return `Searching the web`;
7146
+ case "web_fetch":
7147
+ return `Fetching web page`;
7148
+ case "memory_read":
7149
+ return `Reading from memory`;
7150
+ case "memory_write":
7151
+ return `Saving to memory`;
7152
+ case "task_complete":
7153
+ return String(args["summary"] ?? "Task complete");
7154
+ case "batch_edit":
7155
+ return `Editing multiple files`;
7156
+ case "codebase_map":
7157
+ return `Mapping project structure`;
7158
+ case "diagnostic":
7159
+ return `Running diagnostics`;
7160
+ case "git_info":
7161
+ return `Checking git status`;
7162
+ case "aiwg_setup":
7163
+ return `Setting up development framework`;
7164
+ case "aiwg_health":
7165
+ return `Analyzing project health`;
7166
+ case "aiwg_workflow":
7167
+ return `Running workflow command`;
7168
+ default:
7169
+ return `Using ${toolName}`;
7170
+ }
7171
+ }
7172
+ function describeToolResult(toolName, success) {
7173
+ if (toolName === "task_complete")
7174
+ return "";
7175
+ return success ? "Done" : "That failed, trying to fix it";
7176
+ }
7177
+ function formatBytes2(bytes) {
7178
+ if (bytes < 1024)
7179
+ return `${bytes}B`;
7180
+ if (bytes < 1024 * 1024)
7181
+ return `${(bytes / 1024).toFixed(0)}KB`;
7182
+ return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
7183
+ }
7184
+ var VOICE_MODELS, VOICE_DIR, MODELS_DIR, VoiceEngine;
7185
+ var init_voice = __esm({
7186
+ "packages/cli/dist/tui/voice.js"() {
7187
+ "use strict";
7188
+ init_render();
7189
+ VOICE_MODELS = {
7190
+ glados: {
7191
+ id: "glados",
7192
+ label: "GLaDOS",
7193
+ onnxUrl: "https://raw.githubusercontent.com/robit-man/EGG/main/voice/glados_piper_medium.onnx",
7194
+ configUrl: "https://raw.githubusercontent.com/robit-man/EGG/main/voice/glados_piper_medium.onnx.json"
7195
+ },
7196
+ overwatch: {
7197
+ id: "overwatch",
7198
+ label: "Overwatch",
7199
+ onnxUrl: "https://raw.githubusercontent.com/robit-man/combine_overwatch_onnx/main/overwatch.onnx",
7200
+ configUrl: "https://raw.githubusercontent.com/robit-man/combine_overwatch_onnx/main/overwatch.onnx.json"
7201
+ }
7202
+ };
7203
+ VOICE_DIR = join13(homedir4(), ".open-agents", "voice");
7204
+ MODELS_DIR = join13(VOICE_DIR, "models");
7205
+ VoiceEngine = class {
7206
+ enabled = false;
7207
+ modelId = "glados";
7208
+ ready = false;
7209
+ session = null;
7210
+ // ort.InferenceSession
7211
+ ort = null;
7212
+ // onnxruntime-node module
7213
+ config = null;
7214
+ currentPlayback = null;
7215
+ speakQueue = [];
7216
+ speaking = false;
7217
+ hasEspeak = false;
7218
+ // -------------------------------------------------------------------------
7219
+ // Public API
7220
+ // -------------------------------------------------------------------------
7221
+ async toggle() {
7222
+ if (this.enabled) {
7223
+ this.enabled = false;
7224
+ this.killPlayback();
7225
+ return "Voice feedback disabled.";
7226
+ }
7227
+ try {
7228
+ await this.ensureRuntime();
7229
+ await this.ensureModel(this.modelId);
7230
+ await this.loadSession();
7231
+ this.enabled = true;
7232
+ this.ready = true;
7233
+ return `Voice feedback enabled (${VOICE_MODELS[this.modelId]?.label ?? this.modelId}).`;
7234
+ } catch (err) {
7235
+ return `Failed to enable voice: ${err instanceof Error ? err.message : String(err)}`;
7236
+ }
7237
+ }
7238
+ async setModel(id) {
7239
+ const key = id.toLowerCase();
7240
+ if (!VOICE_MODELS[key]) {
7241
+ return `Unknown voice model: "${id}". Available: ${Object.keys(VOICE_MODELS).join(", ")}`;
7242
+ }
7243
+ this.modelId = key;
7244
+ this.session = null;
7245
+ this.config = null;
7246
+ this.ready = false;
7247
+ if (this.enabled) {
7248
+ try {
7249
+ await this.ensureModel(key);
7250
+ await this.loadSession();
7251
+ this.ready = true;
7252
+ return `Switched to ${VOICE_MODELS[key].label} voice.`;
7253
+ } catch (err) {
7254
+ this.enabled = false;
7255
+ return `Failed to load ${key}: ${err instanceof Error ? err.message : String(err)}`;
7256
+ }
7257
+ }
7258
+ return `Voice model set to ${VOICE_MODELS[key].label}. Enable with /voice.`;
7259
+ }
7260
+ /**
7261
+ * Speak text asynchronously (non-blocking).
7262
+ * Queues if already speaking; drops if queue gets too deep.
7263
+ */
7264
+ speak(text) {
7265
+ if (!this.enabled || !this.ready)
7266
+ return;
7267
+ if (this.speakQueue.length >= 3) {
7268
+ this.speakQueue.length = 0;
7269
+ }
7270
+ this.speakQueue.push(text);
7271
+ if (!this.speaking) {
7272
+ this.drainQueue().catch(() => {
7273
+ });
7274
+ }
7275
+ }
7276
+ dispose() {
7277
+ this.enabled = false;
7278
+ this.killPlayback();
7279
+ this.session = null;
7280
+ this.ort = null;
7281
+ this.config = null;
7282
+ }
7283
+ // -------------------------------------------------------------------------
7284
+ // Queue drain
7285
+ // -------------------------------------------------------------------------
7286
+ async drainQueue() {
7287
+ this.speaking = true;
7288
+ while (this.speakQueue.length > 0) {
7289
+ const text = this.speakQueue.pop();
7290
+ this.speakQueue.length = 0;
7291
+ try {
7292
+ await this.synthesizeAndPlay(text);
7293
+ } catch {
7294
+ }
7295
+ }
7296
+ this.speaking = false;
7297
+ }
7298
+ // -------------------------------------------------------------------------
7299
+ // Synthesis pipeline
7300
+ // -------------------------------------------------------------------------
7301
+ async synthesizeAndPlay(text) {
7302
+ if (!this.session || !this.config || !this.ort)
7303
+ return;
7304
+ const phonemeIds = this.textToPhonemeIds(text);
7305
+ if (phonemeIds.length === 0)
7306
+ return;
7307
+ const inputLength = phonemeIds.length;
7308
+ const inputTensor = new this.ort.Tensor("int64", BigInt64Array.from(phonemeIds.map((id) => BigInt(id))), [1, inputLength]);
7309
+ const lengthTensor = new this.ort.Tensor("int64", BigInt64Array.from([BigInt(inputLength)]), [1]);
7310
+ const scalesTensor = new this.ort.Tensor("float32", Float32Array.from([0.667, 1, 0.8]), [3]);
7311
+ const feeds = {
7312
+ input: inputTensor,
7313
+ input_lengths: lengthTensor,
7314
+ scales: scalesTensor
7315
+ };
7316
+ if (this.config.num_speakers > 1) {
7317
+ feeds["sid"] = new this.ort.Tensor("int64", BigInt64Array.from([BigInt(0)]), [1]);
7318
+ }
7319
+ const result = await this.session.run(feeds);
7320
+ const audioData = result["output"].data;
7321
+ if (audioData.length === 0)
7322
+ return;
7323
+ const wavPath = join13(tmpdir(), `oa-voice-${Date.now()}.wav`);
7324
+ this.writeWav(audioData, this.config.audio.sample_rate, wavPath);
7325
+ await this.playWav(wavPath);
7326
+ try {
7327
+ unlinkSync(wavPath);
7328
+ } catch {
7329
+ }
7330
+ }
7331
+ // -------------------------------------------------------------------------
7332
+ // Phonemization
7333
+ // -------------------------------------------------------------------------
7334
+ textToPhonemeIds(text) {
7335
+ const map = this.config.phoneme_id_map;
7336
+ let phonemes;
7337
+ if (this.hasEspeak) {
7338
+ try {
7339
+ const voice = this.config.espeak?.voice ?? "en-us";
7340
+ phonemes = execSync8(`espeak-ng --ipa -q --sep="" -v ${voice} "${text.replace(/"/g, '\\"')}"`, { encoding: "utf8", timeout: 5e3, stdio: ["pipe", "pipe", "pipe"] }).trim();
7341
+ } catch {
7342
+ phonemes = text.toLowerCase();
7343
+ }
7344
+ } else {
7345
+ phonemes = text.toLowerCase();
7346
+ }
7347
+ const ids = [];
7348
+ if (map["^"])
7349
+ ids.push(...map["^"]);
7350
+ for (const char of phonemes) {
7351
+ if (map[char]) {
7352
+ ids.push(...map[char]);
7353
+ if (map["_"])
7354
+ ids.push(...map["_"]);
7355
+ }
7356
+ }
7357
+ if (map["$"])
7358
+ ids.push(...map["$"]);
7359
+ return ids;
7360
+ }
7361
+ // -------------------------------------------------------------------------
7362
+ // WAV encoder (PCM 16-bit mono)
7363
+ // -------------------------------------------------------------------------
7364
+ writeWav(samples, sampleRate, path) {
7365
+ const numChannels = 1;
7366
+ const bitsPerSample = 16;
7367
+ const byteRate = sampleRate * numChannels * (bitsPerSample / 8);
7368
+ const blockAlign = numChannels * (bitsPerSample / 8);
7369
+ const int16 = new Int16Array(samples.length);
7370
+ for (let i = 0; i < samples.length; i++) {
7371
+ const s = Math.max(-1, Math.min(1, samples[i]));
7372
+ int16[i] = s < 0 ? s * 32768 : s * 32767;
7373
+ }
7374
+ const dataSize = int16.length * 2;
7375
+ const buffer = Buffer.alloc(44 + dataSize);
7376
+ buffer.write("RIFF", 0);
7377
+ buffer.writeUInt32LE(36 + dataSize, 4);
7378
+ buffer.write("WAVE", 8);
7379
+ buffer.write("fmt ", 12);
7380
+ buffer.writeUInt32LE(16, 16);
7381
+ buffer.writeUInt16LE(1, 20);
7382
+ buffer.writeUInt16LE(numChannels, 22);
7383
+ buffer.writeUInt32LE(sampleRate, 24);
7384
+ buffer.writeUInt32LE(byteRate, 28);
7385
+ buffer.writeUInt16LE(blockAlign, 32);
7386
+ buffer.writeUInt16LE(bitsPerSample, 34);
7387
+ buffer.write("data", 36);
7388
+ buffer.writeUInt32LE(dataSize, 40);
7389
+ Buffer.from(int16.buffer, int16.byteOffset, int16.byteLength).copy(buffer, 44);
7390
+ writeFileSync4(path, buffer);
7391
+ }
7392
+ // -------------------------------------------------------------------------
7393
+ // Audio playback (system default speakers)
7394
+ // -------------------------------------------------------------------------
7395
+ async playWav(path) {
7396
+ this.killPlayback();
7397
+ const cmd = this.getPlayCommand(path);
7398
+ if (!cmd)
7399
+ return;
7400
+ return new Promise((resolve12) => {
7401
+ const child = nodeSpawn(cmd[0], cmd.slice(1), {
7402
+ stdio: "ignore",
7403
+ detached: false
7404
+ });
7405
+ this.currentPlayback = child;
7406
+ child.on("close", () => {
7407
+ if (this.currentPlayback === child)
7408
+ this.currentPlayback = null;
7409
+ resolve12();
7410
+ });
7411
+ child.on("error", () => {
7412
+ if (this.currentPlayback === child)
7413
+ this.currentPlayback = null;
7414
+ resolve12();
7415
+ });
7416
+ setTimeout(() => {
7417
+ this.killPlayback();
7418
+ resolve12();
7419
+ }, 15e3);
7420
+ });
7421
+ }
7422
+ getPlayCommand(path) {
7423
+ const os = platform();
7424
+ if (os === "darwin")
7425
+ return ["afplay", path];
7426
+ if (os === "win32") {
7427
+ return [
7428
+ "powershell",
7429
+ "-c",
7430
+ `(New-Object Media.SoundPlayer '${path}').PlaySync()`
7431
+ ];
7432
+ }
7433
+ for (const player of ["paplay", "pw-play", "aplay"]) {
7434
+ try {
7435
+ execSync8(`which ${player}`, { stdio: "pipe" });
7436
+ return [player, path];
7437
+ } catch {
7438
+ }
7439
+ }
7440
+ return null;
7441
+ }
7442
+ killPlayback() {
7443
+ if (this.currentPlayback) {
7444
+ try {
7445
+ this.currentPlayback.kill("SIGTERM");
7446
+ } catch {
7447
+ }
7448
+ this.currentPlayback = null;
7449
+ }
7450
+ }
7451
+ // -------------------------------------------------------------------------
7452
+ // Setup: ONNX runtime installation
7453
+ // -------------------------------------------------------------------------
7454
+ async ensureRuntime() {
7455
+ if (this.ort)
7456
+ return;
7457
+ mkdirSync4(VOICE_DIR, { recursive: true });
7458
+ const pkgPath = join13(VOICE_DIR, "package.json");
7459
+ if (!existsSync8(pkgPath)) {
7460
+ writeFileSync4(pkgPath, JSON.stringify({
7461
+ name: "open-agents-voice",
7462
+ private: true,
7463
+ dependencies: { "onnxruntime-node": "^1.21.0" }
7464
+ }, null, 2));
7465
+ }
7466
+ const voiceRequire = createRequire(join13(VOICE_DIR, "index.js"));
7467
+ try {
7468
+ this.ort = voiceRequire("onnxruntime-node");
7469
+ } catch {
7470
+ renderInfo("Installing ONNX runtime for voice synthesis...");
7471
+ try {
7472
+ execSync8("npm install --no-audit --no-fund", {
7473
+ cwd: VOICE_DIR,
7474
+ stdio: "pipe",
7475
+ timeout: 12e4
7476
+ });
7477
+ this.ort = voiceRequire("onnxruntime-node");
7478
+ } catch (err) {
7479
+ throw new Error(`Failed to install onnxruntime-node. Try manually: cd ${VOICE_DIR} && npm install
7480
+ Error: ${err instanceof Error ? err.message : String(err)}`);
7481
+ }
7482
+ }
7483
+ try {
7484
+ execSync8("espeak-ng --version", { stdio: "pipe" });
7485
+ this.hasEspeak = true;
7486
+ } catch {
7487
+ this.hasEspeak = false;
7488
+ renderWarning("espeak-ng not found \u2014 using basic phonemization. For better voice quality: sudo apt install espeak-ng");
7489
+ }
7490
+ }
7491
+ // -------------------------------------------------------------------------
7492
+ // Setup: Model download
7493
+ // -------------------------------------------------------------------------
7494
+ async ensureModel(id) {
7495
+ const model = VOICE_MODELS[id];
7496
+ if (!model)
7497
+ throw new Error(`Unknown model: ${id}`);
7498
+ const dir = modelDir(id);
7499
+ const onnxPath = modelOnnxPath(id);
7500
+ const configPath = modelConfigPath(id);
7501
+ if (existsSync8(onnxPath) && existsSync8(configPath))
7502
+ return;
7503
+ mkdirSync4(dir, { recursive: true });
7504
+ if (!existsSync8(configPath)) {
7505
+ renderInfo(`Downloading ${model.label} voice config...`);
7506
+ const configResp = await fetch(model.configUrl);
7507
+ if (!configResp.ok)
7508
+ throw new Error(`Failed to download config: HTTP ${configResp.status}`);
7509
+ const configText = await configResp.text();
7510
+ writeFileSync4(configPath, configText);
7511
+ }
7512
+ if (!existsSync8(onnxPath)) {
7513
+ renderInfo(`Downloading ${model.label} voice model (this may take a minute)...`);
7514
+ const onnxResp = await fetch(model.onnxUrl);
7515
+ if (!onnxResp.ok)
7516
+ throw new Error(`Failed to download model: HTTP ${onnxResp.status}`);
7517
+ const reader = onnxResp.body?.getReader();
7518
+ if (!reader)
7519
+ throw new Error("No response body");
7520
+ const contentLength = parseInt(onnxResp.headers.get("content-length") || "0", 10);
7521
+ const chunks = [];
7522
+ let received = 0;
7523
+ while (true) {
7524
+ const { done, value } = await reader.read();
7525
+ if (done)
7526
+ break;
7527
+ chunks.push(value);
7528
+ received += value.length;
7529
+ if (contentLength > 0) {
7530
+ const pct = Math.round(received / contentLength * 100);
7531
+ process.stdout.write(`\r ${c2.dim(` ${pct}% (${formatBytes2(received)} / ${formatBytes2(contentLength)})`)}`);
7532
+ }
7533
+ }
7534
+ process.stdout.write("\r" + " ".repeat(60) + "\r");
7535
+ const fullBuffer = Buffer.concat(chunks);
7536
+ writeFileSync4(onnxPath, fullBuffer);
7537
+ renderInfo(`${model.label} model downloaded (${formatBytes2(fullBuffer.length)}).`);
7538
+ }
7539
+ }
7540
+ // -------------------------------------------------------------------------
7541
+ // Load ONNX session
7542
+ // -------------------------------------------------------------------------
7543
+ async loadSession() {
7544
+ if (!this.ort)
7545
+ throw new Error("ONNX runtime not loaded");
7546
+ const onnxPath = modelOnnxPath(this.modelId);
7547
+ const configPath = modelConfigPath(this.modelId);
7548
+ if (!existsSync8(onnxPath) || !existsSync8(configPath)) {
7549
+ throw new Error(`Model files not found for ${this.modelId}`);
7550
+ }
7551
+ this.config = JSON.parse(readFileSync6(configPath, "utf8"));
7552
+ renderInfo("Loading voice model...");
7553
+ this.session = await this.ort.InferenceSession.create(onnxPath, {
7554
+ executionProviders: ["cpu"],
7555
+ graphOptimizationLevel: "all"
7556
+ });
7557
+ renderInfo("Voice model loaded.");
7558
+ }
7559
+ };
7560
+ }
7561
+ });
7562
+
7006
7563
  // packages/cli/dist/tui/interactive.js
7007
7564
  import * as readline2 from "node:readline";
7008
7565
  import { cwd } from "node:process";
@@ -7059,7 +7616,7 @@ function buildTools(repoRoot) {
7059
7616
  ];
7060
7617
  return [...executionTools.map(adaptTool), createTaskCompleteTool()];
7061
7618
  }
7062
- async function runTask(task, config, repoRoot) {
7619
+ async function runTask(task, config, repoRoot, voice) {
7063
7620
  const backend = new OllamaAgenticBackend(config.backendUrl.replace(/\/$/, ""), config.model);
7064
7621
  const runner = new AgenticRunner(backend, {
7065
7622
  maxTurns: 30,
@@ -7074,9 +7631,16 @@ async function runTask(task, config, repoRoot) {
7074
7631
  switch (event.type) {
7075
7632
  case "tool_call":
7076
7633
  renderToolCallStart(event.toolName ?? "unknown", event.toolArgs ?? {});
7634
+ if (voice?.enabled) {
7635
+ const desc = describeToolCall(event.toolName ?? "unknown", event.toolArgs ?? {});
7636
+ voice.speak(desc);
7637
+ }
7077
7638
  break;
7078
7639
  case "tool_result":
7079
7640
  renderToolResult(event.toolName ?? "unknown", event.success ?? false, event.content ?? "");
7641
+ if (voice?.enabled && !(event.success ?? true)) {
7642
+ voice.speak(describeToolResult(event.toolName ?? "unknown", false));
7643
+ }
7080
7644
  break;
7081
7645
  case "model_response":
7082
7646
  if (config.verbose && event.content) {
@@ -7124,6 +7688,7 @@ async function startInteractive(config, repoPath) {
7124
7688
  process.exit(1);
7125
7689
  }
7126
7690
  renderHeader(config.model);
7691
+ const voiceEngine = new VoiceEngine();
7127
7692
  let currentConfig = { ...config };
7128
7693
  const rl = readline2.createInterface({
7129
7694
  input: process.stdin,
@@ -7155,7 +7720,14 @@ async function startInteractive(config, repoPath) {
7155
7720
  renderCompactHeader(currentConfig.model);
7156
7721
  },
7157
7722
  exit() {
7723
+ voiceEngine.dispose();
7158
7724
  rl.close();
7725
+ },
7726
+ async voiceToggle() {
7727
+ return voiceEngine.toggle();
7728
+ },
7729
+ async voiceSetModel(id) {
7730
+ return voiceEngine.setModel(id);
7159
7731
  }
7160
7732
  };
7161
7733
  rl.prompt();
@@ -7180,7 +7752,7 @@ ${c2.dim("Goodbye!")}
7180
7752
  }
7181
7753
  renderUserMessage(input);
7182
7754
  try {
7183
- await runTask(input, currentConfig, repoRoot);
7755
+ await runTask(input, currentConfig, repoRoot, voiceEngine);
7184
7756
  } catch (err) {
7185
7757
  renderError(err instanceof Error ? err.message : String(err));
7186
7758
  }
@@ -7239,6 +7811,7 @@ var init_interactive = __esm({
7239
7811
  init_commands();
7240
7812
  init_setup();
7241
7813
  init_render();
7814
+ init_voice();
7242
7815
  }
7243
7816
  });
7244
7817
 
@@ -7273,7 +7846,7 @@ import { glob } from "glob";
7273
7846
  import ignore from "ignore";
7274
7847
  import { readFile as readFile8, stat as stat2 } from "node:fs/promises";
7275
7848
  import { createHash } from "node:crypto";
7276
- import { join as join13, relative as relative2, extname as extname3, basename } from "node:path";
7849
+ import { join as join14, relative as relative2, extname as extname3, basename } from "node:path";
7277
7850
  var DEFAULT_EXCLUDE, LANGUAGE_MAP, CodebaseIndexer;
7278
7851
  var init_codebase_indexer = __esm({
7279
7852
  "packages/indexer/dist/codebase-indexer.js"() {
@@ -7317,7 +7890,7 @@ var init_codebase_indexer = __esm({
7317
7890
  const ig = ignore.default();
7318
7891
  if (this.config.respectGitignore) {
7319
7892
  try {
7320
- const gitignoreContent = await readFile8(join13(this.config.rootDir, ".gitignore"), "utf-8");
7893
+ const gitignoreContent = await readFile8(join14(this.config.rootDir, ".gitignore"), "utf-8");
7321
7894
  ig.add(gitignoreContent);
7322
7895
  } catch {
7323
7896
  }
@@ -7332,7 +7905,7 @@ var init_codebase_indexer = __esm({
7332
7905
  for (const relativePath of files) {
7333
7906
  if (ig.ignores(relativePath))
7334
7907
  continue;
7335
- const fullPath = join13(this.config.rootDir, relativePath);
7908
+ const fullPath = join14(this.config.rootDir, relativePath);
7336
7909
  try {
7337
7910
  const fileStat = await stat2(fullPath);
7338
7911
  if (fileStat.size > this.config.maxFileSize)
@@ -7378,7 +7951,7 @@ var init_codebase_indexer = __esm({
7378
7951
  if (!child) {
7379
7952
  child = {
7380
7953
  name: part,
7381
- path: join13(current.path, part),
7954
+ path: join14(current.path, part),
7382
7955
  type: "directory",
7383
7956
  children: []
7384
7957
  };
@@ -7453,13 +8026,13 @@ __export(index_repo_exports, {
7453
8026
  indexRepoCommand: () => indexRepoCommand
7454
8027
  });
7455
8028
  import { resolve as resolve11 } from "node:path";
7456
- import { existsSync as existsSync8, statSync as statSync4 } from "node:fs";
8029
+ import { existsSync as existsSync9, statSync as statSync4 } from "node:fs";
7457
8030
  import { cwd as cwd2 } from "node:process";
7458
8031
  async function indexRepoCommand(opts, _config) {
7459
8032
  const repoRoot = resolve11(opts.repoPath ?? cwd2());
7460
8033
  printHeader("Index Repository");
7461
8034
  printInfo(`Indexing: ${repoRoot}`);
7462
- if (!existsSync8(repoRoot)) {
8035
+ if (!existsSync9(repoRoot)) {
7463
8036
  printError(`Path does not exist: ${repoRoot}`);
7464
8037
  process.exit(1);
7465
8038
  }
@@ -7705,8 +8278,8 @@ var config_exports = {};
7705
8278
  __export(config_exports, {
7706
8279
  configCommand: () => configCommand
7707
8280
  });
7708
- import { join as join14 } from "node:path";
7709
- import { homedir as homedir4 } from "node:os";
8281
+ import { join as join15 } from "node:path";
8282
+ import { homedir as homedir5 } from "node:os";
7710
8283
  async function configCommand(opts, config) {
7711
8284
  if (opts.subCommand === "set") {
7712
8285
  return handleSet(opts, config);
@@ -7728,7 +8301,7 @@ function handleShow(opts, config) {
7728
8301
  printKeyValue("verbose", String(config.verbose), 2);
7729
8302
  printKeyValue("dbPath", config.dbPath, 2);
7730
8303
  printSection("Config File");
7731
- printInfo(`~/.open-agents/config.json (${join14(homedir4(), ".open-agents", "config.json")})`);
8304
+ printInfo(`~/.open-agents/config.json (${join15(homedir5(), ".open-agents", "config.json")})`);
7732
8305
  printSection("Environment Variables");
7733
8306
  printInfo("OPEN_AGENTS_BACKEND_URL \u2014 override backendUrl");
7734
8307
  printInfo("OPEN_AGENTS_MODEL \u2014 override model");
@@ -7802,7 +8375,7 @@ var serve_exports = {};
7802
8375
  __export(serve_exports, {
7803
8376
  serveCommand: () => serveCommand
7804
8377
  });
7805
- import { spawn } from "node:child_process";
8378
+ import { spawn as spawn2 } from "node:child_process";
7806
8379
  async function serveCommand(opts, config) {
7807
8380
  const backendType = config.backendType;
7808
8381
  if (backendType === "ollama") {
@@ -7895,7 +8468,7 @@ async function serveVllm(opts, config) {
7895
8468
  }
7896
8469
  async function runVllmServer(args, verbose) {
7897
8470
  return new Promise((resolve12, reject) => {
7898
- const child = spawn("python", args, {
8471
+ const child = spawn2("python", args, {
7899
8472
  stdio: verbose ? "inherit" : ["ignore", "pipe", "pipe"],
7900
8473
  env: { ...process.env }
7901
8474
  });
@@ -7959,9 +8532,9 @@ var eval_exports = {};
7959
8532
  __export(eval_exports, {
7960
8533
  evalCommand: () => evalCommand
7961
8534
  });
7962
- import { tmpdir } from "node:os";
7963
- import { mkdirSync as mkdirSync4, writeFileSync as writeFileSync4 } from "node:fs";
7964
- import { join as join15 } from "node:path";
8535
+ import { tmpdir as tmpdir2 } from "node:os";
8536
+ import { mkdirSync as mkdirSync5, writeFileSync as writeFileSync5 } from "node:fs";
8537
+ import { join as join16 } from "node:path";
7965
8538
  async function evalCommand(opts, config) {
7966
8539
  const suiteName = opts.suite ?? "basic";
7967
8540
  const suite = SUITES[suiteName];
@@ -8082,9 +8655,9 @@ async function evalCommand(opts, config) {
8082
8655
  process.exit(failed > 0 ? 1 : 0);
8083
8656
  }
8084
8657
  function createTempEvalRepo() {
8085
- const dir = join15(tmpdir(), `open-agents-eval-${Date.now()}`);
8086
- mkdirSync4(dir, { recursive: true });
8087
- writeFileSync4(join15(dir, "package.json"), JSON.stringify({ name: "eval-repo", version: "0.0.0" }, null, 2) + "\n", "utf8");
8658
+ const dir = join16(tmpdir2(), `open-agents-eval-${Date.now()}`);
8659
+ mkdirSync5(dir, { recursive: true });
8660
+ writeFileSync5(join16(dir, "package.json"), JSON.stringify({ name: "eval-repo", version: "0.0.0" }, null, 2) + "\n", "utf8");
8088
8661
  return dir;
8089
8662
  }
8090
8663
  var BASIC_SUITE, FULL_SUITE, SUITES;
@@ -8142,9 +8715,9 @@ init_config();
8142
8715
  init_output();
8143
8716
  init_updater();
8144
8717
  import { parseArgs as nodeParseArgs2 } from "node:util";
8145
- import { createRequire } from "node:module";
8718
+ import { createRequire as createRequire2 } from "node:module";
8146
8719
  import { fileURLToPath } from "node:url";
8147
- import { dirname as dirname3, join as join16 } from "node:path";
8720
+ import { dirname as dirname3, join as join17 } from "node:path";
8148
8721
 
8149
8722
  // packages/cli/dist/cli.js
8150
8723
  import { createInterface } from "node:readline";
@@ -8250,8 +8823,8 @@ init_spinner();
8250
8823
  init_output();
8251
8824
  function getVersion() {
8252
8825
  try {
8253
- const require2 = createRequire(import.meta.url);
8254
- const pkgPath = join16(dirname3(fileURLToPath(import.meta.url)), "..", "package.json");
8826
+ const require2 = createRequire2(import.meta.url);
8827
+ const pkgPath = join17(dirname3(fileURLToPath(import.meta.url)), "..", "package.json");
8255
8828
  const pkg = require2(pkgPath);
8256
8829
  return pkg.version;
8257
8830
  } catch {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "open-agents-ai",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "AI coding agent powered by open-source models (Ollama/vLLM) — Claude Code-style TUI with agentic tool-calling loop",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -25,7 +25,10 @@
25
25
  "tool-calling",
26
26
  "agentic",
27
27
  "code-generation",
28
- "developer-tools"
28
+ "developer-tools",
29
+ "tts",
30
+ "voice",
31
+ "onnx"
29
32
  ],
30
33
  "author": "robit-man",
31
34
  "license": "MIT",