open-agents-ai 0.79.0 → 0.81.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 (2) hide show
  1. package/dist/index.js +132 -71
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -11203,10 +11203,11 @@ ${truncated}`, durationMs: performance.now() - start };
11203
11203
 
11204
11204
  // packages/execution/dist/tools/nexus.js
11205
11205
  import { readFile as readFile13, writeFile as writeFile12, mkdir as mkdir8, chmod, unlink, readdir as readdir3 } from "node:fs/promises";
11206
- import { existsSync as existsSync21 } from "node:fs";
11206
+ import { existsSync as existsSync21, readFileSync as readFileSync15 } from "node:fs";
11207
11207
  import { resolve as resolve26, join as join28 } from "node:path";
11208
11208
  import { randomBytes as randomBytes6, createCipheriv, createDecipheriv, scryptSync, createHash } from "node:crypto";
11209
11209
  import { execSync as execSync21, spawn as spawn10 } from "node:child_process";
11210
+ import { hostname, userInfo } from "node:os";
11210
11211
  function containsKeyMaterial(input) {
11211
11212
  for (const pattern of KEY_PATTERNS) {
11212
11213
  if (pattern.test(input))
@@ -11396,6 +11397,17 @@ setInterval(() => {
11396
11397
  } catch {}
11397
11398
  }, 500);
11398
11399
 
11400
+ // Crash protection \u2014 prevent unhandled errors from killing the daemon
11401
+ process.on('uncaughtException', (err) => {
11402
+ console.error('Nexus daemon uncaughtException:', err.message || err);
11403
+ writeStatus({ error: 'uncaughtException: ' + (err.message || String(err)) });
11404
+ });
11405
+ process.on('unhandledRejection', (reason) => {
11406
+ const msg = reason instanceof Error ? reason.message : String(reason);
11407
+ console.error('Nexus daemon unhandledRejection:', msg);
11408
+ // Do NOT exit \u2014 keep daemon alive
11409
+ });
11410
+
11399
11411
  // Connect
11400
11412
  (async () => {
11401
11413
  try {
@@ -11418,7 +11430,7 @@ setInterval(() => {
11418
11430
  // Graceful shutdown
11419
11431
  process.on('SIGTERM', async () => {
11420
11432
  for (const [, room] of rooms) { try { await room.leave(); } catch {} }
11421
- await nexus.disconnect();
11433
+ try { await nexus.disconnect(); } catch {}
11422
11434
  try { unlinkSync(pidFile); } catch {}
11423
11435
  try { unlinkSync(statusFile); } catch {}
11424
11436
  process.exit(0);
@@ -11592,7 +11604,7 @@ process.on('SIGINT', () => process.emit('SIGTERM'));
11592
11604
  if (!existsSync21(pidFile))
11593
11605
  return null;
11594
11606
  try {
11595
- const pid = parseInt(__require("node:fs").readFileSync(pidFile, "utf8").trim(), 10);
11607
+ const pid = parseInt(readFileSync15(pidFile, "utf8").trim(), 10);
11596
11608
  process.kill(pid, 0);
11597
11609
  return pid;
11598
11610
  } catch {
@@ -11635,11 +11647,23 @@ process.on('SIGINT', () => process.emit('SIGTERM'));
11635
11647
  if (existsSync21(statusFile2)) {
11636
11648
  try {
11637
11649
  const status = JSON.parse(await readFile13(statusFile2, "utf8"));
11638
- return `Already connected (pid: ${existingPid}, peerId: ${status.peerId || "starting..."})`;
11650
+ if (status.connected && status.peerId) {
11651
+ return `Already connected (pid: ${existingPid}, peerId: ${status.peerId})`;
11652
+ }
11639
11653
  } catch {
11640
11654
  }
11641
11655
  }
11642
- return `Daemon already running (pid: ${existingPid}).`;
11656
+ try {
11657
+ process.kill(existingPid, "SIGTERM");
11658
+ } catch {
11659
+ }
11660
+ await new Promise((r) => setTimeout(r, 500));
11661
+ }
11662
+ for (const f of ["daemon.pid", "status.json", "cmd.json", "resp.json"]) {
11663
+ const p = join28(this.nexusDir, f);
11664
+ if (existsSync21(p))
11665
+ await unlink(p).catch(() => {
11666
+ });
11643
11667
  }
11644
11668
  const nodeModulesDir = resolve26(this.repoRoot, "node_modules");
11645
11669
  let nexusResolved = false;
@@ -11907,7 +11931,7 @@ process.on('SIGINT', () => process.emit('SIGTERM'));
11907
11931
  const privKey = randomBytes6(32);
11908
11932
  const address = "0x" + createHash("sha256").update(privKey).digest("hex").slice(0, 40);
11909
11933
  const salt = randomBytes6(32);
11910
- const key = scryptSync(`${__require("node:os").hostname()}:${__require("node:os").userInfo().username}:nexus-wallet`, salt, 32, { N: 16384, r: 8, p: 1 });
11934
+ const key = scryptSync(`${hostname()}:${userInfo().username}:nexus-wallet`, salt, 32, { N: 16384, r: 8, p: 1 });
11911
11935
  const iv = randomBytes6(16);
11912
11936
  const cipher = createCipheriv("aes-256-gcm", key, iv);
11913
11937
  let enc = cipher.update(privKey.toString("hex"), "utf8", "hex");
@@ -12788,7 +12812,7 @@ var init_dist3 = __esm({
12788
12812
  });
12789
12813
 
12790
12814
  // packages/orchestrator/dist/promptLoader.js
12791
- import { readFileSync as readFileSync15, existsSync as existsSync22 } from "node:fs";
12815
+ import { readFileSync as readFileSync16, existsSync as existsSync22 } from "node:fs";
12792
12816
  import { join as join29, dirname as dirname10 } from "node:path";
12793
12817
  import { fileURLToPath as fileURLToPath7 } from "node:url";
12794
12818
  function loadPrompt(promptPath, vars) {
@@ -12798,7 +12822,7 @@ function loadPrompt(promptPath, vars) {
12798
12822
  if (!existsSync22(fullPath)) {
12799
12823
  throw new Error(`Prompt file not found: ${fullPath}`);
12800
12824
  }
12801
- content = readFileSync15(fullPath, "utf-8");
12825
+ content = readFileSync16(fullPath, "utf-8");
12802
12826
  cache.set(promptPath, content);
12803
12827
  }
12804
12828
  if (!vars)
@@ -23617,7 +23641,7 @@ var init_types = __esm({
23617
23641
 
23618
23642
  // packages/cli/dist/tui/p2p/secret-vault.js
23619
23643
  import { createCipheriv as createCipheriv2, createDecipheriv as createDecipheriv2, randomBytes as randomBytes8, scryptSync as scryptSync2, createHash as createHash2 } from "node:crypto";
23620
- import { readFileSync as readFileSync16, writeFileSync as writeFileSync7, existsSync as existsSync24, mkdirSync as mkdirSync7 } from "node:fs";
23644
+ import { readFileSync as readFileSync17, writeFileSync as writeFileSync7, existsSync as existsSync24, mkdirSync as mkdirSync7 } from "node:fs";
23621
23645
  import { join as join33, dirname as dirname12 } from "node:path";
23622
23646
  var PLACEHOLDER_PREFIX, PLACEHOLDER_SUFFIX, CIPHER_ALGO, SALT_LEN, IV_LEN, KEY_LEN, SecretVault;
23623
23647
  var init_secret_vault = __esm({
@@ -23841,7 +23865,7 @@ var init_secret_vault = __esm({
23841
23865
  load(passphrase) {
23842
23866
  if (!this.storePath || !existsSync24(this.storePath))
23843
23867
  return 0;
23844
- const blob = readFileSync16(this.storePath);
23868
+ const blob = readFileSync17(this.storePath);
23845
23869
  if (blob.length < SALT_LEN + IV_LEN + 16) {
23846
23870
  throw new Error("Vault file is corrupted (too small)");
23847
23871
  }
@@ -24639,11 +24663,14 @@ var init_call_agent = __esm({
24639
24663
  const feed = getActivityFeed();
24640
24664
  const systemPrompt = this.buildSystemPrompt();
24641
24665
  const runnerOpts = {
24642
- maxTurns: this.tier === "admin" ? 15 : 5,
24643
- maxTokens: 4096,
24666
+ maxTurns: this.tier === "admin" ? 8 : 3,
24667
+ // Keep low for fast voice responses
24668
+ maxTokens: 2048,
24669
+ // Short responses for TTS
24644
24670
  temperature: 0.3,
24645
- requestTimeoutMs: 3e4,
24646
- taskTimeoutMs: 12e4,
24671
+ requestTimeoutMs: 2e4,
24672
+ // Fast timeout for voice responsiveness
24673
+ taskTimeoutMs: 6e4,
24647
24674
  modelTier: this.opts.modelTier ?? "large",
24648
24675
  contextWindowSize: this.opts.contextWindowSize,
24649
24676
  personality: this.opts.personality,
@@ -24742,17 +24769,17 @@ var init_call_agent = __esm({
24742
24769
  const historyContext = this.conversationHistory.slice(-10).map((h) => `${h.role === "user" ? "User" : "You"}: ${h.text}`).join("\n");
24743
24770
  const feed = getActivityFeed();
24744
24771
  const activitySummary = feed.getSummary(this.tier === "admin" ? 20 : 10, this.tier === "admin");
24772
+ const wantsAction = /\b(read|open|show|run|execute|check|look at|find|search|grep|edit|write|fix|test|build|deploy|install)\b/i.test(text) && !/\b(how are you|what's up|hello|hi|hey|can you hear|stop|quit|bye)\b/i.test(text);
24745
24773
  const taskPrompt = [
24746
- `The user said (via voice): "${text}"`,
24747
- "",
24748
- "Recent conversation:",
24749
- historyContext,
24774
+ `User said: "${text}"`,
24750
24775
  "",
24751
- "Recent agent activity:",
24752
- activitySummary,
24753
- "",
24754
- "Respond conversationally and concisely. Your response will be spoken aloud via TTS.",
24755
- "Call task_complete with your spoken response as the summary."
24776
+ historyContext ? `Conversation so far:
24777
+ ${historyContext}
24778
+ ` : "",
24779
+ `Background activity:
24780
+ ${activitySummary}
24781
+ `,
24782
+ wantsAction ? "The user is requesting an action. You may use tools, then call task_complete with a brief spoken summary of what you did." : "RESPOND IMMEDIATELY \u2014 call task_complete with your spoken reply. Do NOT use any tools. Just answer conversationally in 1-3 sentences."
24756
24783
  ].join("\n");
24757
24784
  const result = await this.runner.run(taskPrompt, `Working directory: ${this.repoRoot}`);
24758
24785
  if (result.summary) {
@@ -24773,18 +24800,24 @@ var init_call_agent = __esm({
24773
24800
  }
24774
24801
  buildSystemPrompt() {
24775
24802
  const base = [
24776
- "You are a voice assistant for an AI coding agent. Users are speaking to you through a live audio call.",
24777
- "Keep responses SHORT and conversational \u2014 they will be spoken aloud via text-to-speech.",
24778
- "Avoid code blocks, markdown formatting, or long technical explanations in your spoken responses.",
24779
- "If the user asks about what the agent is doing, summarize the recent activity concisely."
24803
+ "You are a voice assistant on a LIVE AUDIO CALL. This is a real-time conversation.",
24804
+ "",
24805
+ "CRITICAL RULES FOR VOICE CALLS:",
24806
+ "1. ALWAYS respond IMMEDIATELY with speech. Do NOT use tools before responding.",
24807
+ "2. Your response goes through text-to-speech \u2014 keep it SHORT (1-3 sentences).",
24808
+ "3. NEVER use code blocks, markdown, or long technical text.",
24809
+ "4. Be conversational and natural, like talking to a colleague.",
24810
+ "5. Call task_complete with your spoken response as the summary.",
24811
+ "6. Only use tools (file_read, grep, shell, etc.) if the user EXPLICITLY asks you to look something up, run a command, or make a change. For normal conversation, NEVER call tools.",
24812
+ "7. If the user asks what's happening, summarize from the activity context below \u2014 do NOT run tools to find out."
24780
24813
  ];
24781
24814
  if (this.opts.emotionContext) {
24782
- base.push("", "Current emotional context:", this.opts.emotionContext);
24815
+ base.push("", "Mood:", this.opts.emotionContext);
24783
24816
  }
24784
24817
  if (this.tier === "admin") {
24785
- base.push("", "This is an ADMIN call \u2014 you have full access to all tools and can execute commands, read/write files, search code, etc.", "You can take actions on behalf of the user just like the main agent.");
24818
+ base.push("", "ADMIN call \u2014 you CAN use tools IF the user explicitly requests an action (e.g. 'read that file', 'run the tests').", "But for general chat, status questions, or greetings \u2014 respond immediately WITHOUT tools.");
24786
24819
  } else {
24787
- base.push("", "This is a PUBLIC call \u2014 you have read-only access. You can read files and search code but cannot modify anything.", "You can answer questions about the project and explain what the agent is working on.", "Be helpful and friendly but do not execute any write operations.");
24820
+ base.push("", "PUBLIC call \u2014 read-only access. Answer questions about the project conversationally.");
24788
24821
  }
24789
24822
  return base.join("\n");
24790
24823
  }
@@ -25052,7 +25085,7 @@ var init_render2 = __esm({
25052
25085
  });
25053
25086
 
25054
25087
  // packages/prompts/dist/promptLoader.js
25055
- import { readFileSync as readFileSync17, existsSync as existsSync25 } from "node:fs";
25088
+ import { readFileSync as readFileSync18, existsSync as existsSync25 } from "node:fs";
25056
25089
  import { join as join34, dirname as dirname13 } from "node:path";
25057
25090
  import { fileURLToPath as fileURLToPath9 } from "node:url";
25058
25091
  function loadPrompt2(promptPath, vars) {
@@ -25062,7 +25095,7 @@ function loadPrompt2(promptPath, vars) {
25062
25095
  if (!existsSync25(fullPath)) {
25063
25096
  throw new Error(`Prompt file not found: ${fullPath}`);
25064
25097
  }
25065
- content = readFileSync17(fullPath, "utf-8");
25098
+ content = readFileSync18(fullPath, "utf-8");
25066
25099
  cache2.set(promptPath, content);
25067
25100
  }
25068
25101
  if (!vars)
@@ -25204,7 +25237,7 @@ var init_dist6 = __esm({
25204
25237
  });
25205
25238
 
25206
25239
  // packages/cli/dist/tui/oa-directory.js
25207
- import { existsSync as existsSync26, mkdirSync as mkdirSync8, readFileSync as readFileSync18, writeFileSync as writeFileSync8, readdirSync as readdirSync7, statSync as statSync9, unlinkSync as unlinkSync3 } from "node:fs";
25240
+ import { existsSync as existsSync26, mkdirSync as mkdirSync8, readFileSync as readFileSync19, writeFileSync as writeFileSync8, readdirSync as readdirSync7, statSync as statSync9, unlinkSync as unlinkSync3 } from "node:fs";
25208
25241
  import { join as join36, relative as relative2, basename as basename9, extname as extname8 } from "node:path";
25209
25242
  import { homedir as homedir9 } from "node:os";
25210
25243
  function initOaDirectory(repoRoot) {
@@ -25216,7 +25249,7 @@ function initOaDirectory(repoRoot) {
25216
25249
  const gitignorePath = join36(repoRoot, ".gitignore");
25217
25250
  const settingsPattern = ".oa/settings.json";
25218
25251
  if (existsSync26(gitignorePath)) {
25219
- const content = readFileSync18(gitignorePath, "utf-8");
25252
+ const content = readFileSync19(gitignorePath, "utf-8");
25220
25253
  if (!content.includes(settingsPattern)) {
25221
25254
  writeFileSync8(gitignorePath, content.trimEnd() + "\n" + settingsPattern + "\n", "utf-8");
25222
25255
  }
@@ -25232,7 +25265,7 @@ function loadProjectSettings(repoRoot) {
25232
25265
  const settingsPath = join36(repoRoot, OA_DIR, "settings.json");
25233
25266
  try {
25234
25267
  if (existsSync26(settingsPath)) {
25235
- return JSON.parse(readFileSync18(settingsPath, "utf-8"));
25268
+ return JSON.parse(readFileSync19(settingsPath, "utf-8"));
25236
25269
  }
25237
25270
  } catch {
25238
25271
  }
@@ -25249,7 +25282,7 @@ function loadGlobalSettings() {
25249
25282
  const settingsPath = join36(homedir9(), ".open-agents", "settings.json");
25250
25283
  try {
25251
25284
  if (existsSync26(settingsPath)) {
25252
- return JSON.parse(readFileSync18(settingsPath, "utf-8"));
25285
+ return JSON.parse(readFileSync19(settingsPath, "utf-8"));
25253
25286
  }
25254
25287
  } catch {
25255
25288
  }
@@ -25280,7 +25313,7 @@ function discoverContextFiles(repoRoot, maxContentLen = 8e3) {
25280
25313
  if (existsSync26(filePath) && !seen.has(filePath)) {
25281
25314
  seen.add(filePath);
25282
25315
  try {
25283
- let content = readFileSync18(filePath, "utf-8");
25316
+ let content = readFileSync19(filePath, "utf-8");
25284
25317
  if (content.length > maxContentLen) {
25285
25318
  content = content.slice(0, maxContentLen) + "\n\n...(truncated)";
25286
25319
  }
@@ -25298,7 +25331,7 @@ function discoverContextFiles(repoRoot, maxContentLen = 8e3) {
25298
25331
  if (existsSync26(projectMap) && !seen.has(projectMap)) {
25299
25332
  seen.add(projectMap);
25300
25333
  try {
25301
- let content = readFileSync18(projectMap, "utf-8");
25334
+ let content = readFileSync19(projectMap, "utf-8");
25302
25335
  if (content.length > maxContentLen) {
25303
25336
  content = content.slice(0, maxContentLen) + "\n\n...(truncated)";
25304
25337
  }
@@ -25330,7 +25363,7 @@ function discoverContextFiles(repoRoot, maxContentLen = 8e3) {
25330
25363
  function readIndexMeta(repoRoot) {
25331
25364
  const metaPath = join36(repoRoot, OA_DIR, "index", "meta.json");
25332
25365
  try {
25333
- return JSON.parse(readFileSync18(metaPath, "utf-8"));
25366
+ return JSON.parse(readFileSync19(metaPath, "utf-8"));
25334
25367
  } catch {
25335
25368
  return null;
25336
25369
  }
@@ -25402,7 +25435,7 @@ function loadRecentSessions(repoRoot, limit = 5) {
25402
25435
  }).sort((a, b) => b.mtime - a.mtime).slice(0, limit);
25403
25436
  return files.map((f) => {
25404
25437
  try {
25405
- return JSON.parse(readFileSync18(join36(historyDir, f.file), "utf-8"));
25438
+ return JSON.parse(readFileSync19(join36(historyDir, f.file), "utf-8"));
25406
25439
  } catch {
25407
25440
  return null;
25408
25441
  }
@@ -25421,7 +25454,7 @@ function loadPendingTask(repoRoot) {
25421
25454
  try {
25422
25455
  if (!existsSync26(filePath))
25423
25456
  return null;
25424
- const data = JSON.parse(readFileSync18(filePath, "utf-8"));
25457
+ const data = JSON.parse(readFileSync19(filePath, "utf-8"));
25425
25458
  try {
25426
25459
  unlinkSync3(filePath);
25427
25460
  } catch {
@@ -25438,7 +25471,7 @@ function saveSessionContext(repoRoot, entry) {
25438
25471
  let ctx;
25439
25472
  try {
25440
25473
  if (existsSync26(filePath)) {
25441
- ctx = JSON.parse(readFileSync18(filePath, "utf-8"));
25474
+ ctx = JSON.parse(readFileSync19(filePath, "utf-8"));
25442
25475
  } else {
25443
25476
  ctx = { entries: [], maxEntries: MAX_CONTEXT_ENTRIES, updatedAt: "" };
25444
25477
  }
@@ -25457,7 +25490,7 @@ function loadSessionContext(repoRoot) {
25457
25490
  try {
25458
25491
  if (!existsSync26(filePath))
25459
25492
  return null;
25460
- return JSON.parse(readFileSync18(filePath, "utf-8"));
25493
+ return JSON.parse(readFileSync19(filePath, "utf-8"));
25461
25494
  } catch {
25462
25495
  return null;
25463
25496
  }
@@ -25509,7 +25542,7 @@ function detectManifests(repoRoot) {
25509
25542
  let name;
25510
25543
  if (check.nameField) {
25511
25544
  try {
25512
- const data = JSON.parse(readFileSync18(filePath, "utf-8"));
25545
+ const data = JSON.parse(readFileSync19(filePath, "utf-8"));
25513
25546
  name = data[check.nameField];
25514
25547
  } catch {
25515
25548
  }
@@ -28599,7 +28632,7 @@ var init_commands = __esm({
28599
28632
  });
28600
28633
 
28601
28634
  // packages/cli/dist/tui/project-context.js
28602
- import { existsSync as existsSync28, readFileSync as readFileSync19, readdirSync as readdirSync8 } from "node:fs";
28635
+ import { existsSync as existsSync28, readFileSync as readFileSync20, readdirSync as readdirSync8 } from "node:fs";
28603
28636
  import { join as join38, basename as basename10 } from "node:path";
28604
28637
  import { execSync as execSync25 } from "node:child_process";
28605
28638
  import { homedir as homedir11, platform as platform2, release } from "node:os";
@@ -28638,7 +28671,7 @@ function loadProjectMap(repoRoot) {
28638
28671
  const mapPath = join38(repoRoot, OA_DIR, "context", "project-map.md");
28639
28672
  if (existsSync28(mapPath)) {
28640
28673
  try {
28641
- const content = readFileSync19(mapPath, "utf-8");
28674
+ const content = readFileSync20(mapPath, "utf-8");
28642
28675
  return content;
28643
28676
  } catch {
28644
28677
  }
@@ -28703,7 +28736,7 @@ function loadMemoryDir(memDir, scope) {
28703
28736
  const files = readdirSync8(memDir).filter((f) => f.endsWith(".json"));
28704
28737
  for (const file of files.slice(0, 10)) {
28705
28738
  try {
28706
- const raw = readFileSync19(join38(memDir, file), "utf-8");
28739
+ const raw = readFileSync20(join38(memDir, file), "utf-8");
28707
28740
  const entries = JSON.parse(raw);
28708
28741
  const topic = basename10(file, ".json");
28709
28742
  const keys = Object.keys(entries);
@@ -29730,14 +29763,14 @@ var init_carousel = __esm({
29730
29763
  });
29731
29764
 
29732
29765
  // packages/cli/dist/tui/carousel-descriptors.js
29733
- import { existsSync as existsSync29, readFileSync as readFileSync20, writeFileSync as writeFileSync10, mkdirSync as mkdirSync10, readdirSync as readdirSync9 } from "node:fs";
29766
+ import { existsSync as existsSync29, readFileSync as readFileSync21, writeFileSync as writeFileSync10, mkdirSync as mkdirSync10, readdirSync as readdirSync9 } from "node:fs";
29734
29767
  import { join as join39, basename as basename11 } from "node:path";
29735
29768
  function loadToolProfile(repoRoot) {
29736
29769
  const filePath = join39(repoRoot, OA_DIR, "context", TOOL_PROFILE_FILE);
29737
29770
  try {
29738
29771
  if (!existsSync29(filePath))
29739
29772
  return null;
29740
- return JSON.parse(readFileSync20(filePath, "utf-8"));
29773
+ return JSON.parse(readFileSync21(filePath, "utf-8"));
29741
29774
  } catch {
29742
29775
  return null;
29743
29776
  }
@@ -29807,7 +29840,7 @@ function loadCachedDescriptors(repoRoot) {
29807
29840
  try {
29808
29841
  if (!existsSync29(filePath))
29809
29842
  return null;
29810
- const cached = JSON.parse(readFileSync20(filePath, "utf-8"));
29843
+ const cached = JSON.parse(readFileSync21(filePath, "utf-8"));
29811
29844
  return cached.phrases.length > 0 ? cached.phrases : null;
29812
29845
  } catch {
29813
29846
  return null;
@@ -29873,7 +29906,7 @@ function extractFromPackageJson(repoRoot, tags) {
29873
29906
  try {
29874
29907
  if (!existsSync29(pkgPath))
29875
29908
  return;
29876
- const pkg = JSON.parse(readFileSync20(pkgPath, "utf-8"));
29909
+ const pkg = JSON.parse(readFileSync21(pkgPath, "utf-8"));
29877
29910
  if (pkg.name && typeof pkg.name === "string") {
29878
29911
  const parts = pkg.name.replace(/^@/, "").split("/");
29879
29912
  for (const p of parts)
@@ -29948,7 +29981,7 @@ function extractFromMemory(repoRoot, tags) {
29948
29981
  const topic = file.replace(/\.json$/, "").replace(/[-_]/g, " ");
29949
29982
  tags.push(topic);
29950
29983
  try {
29951
- const data = JSON.parse(readFileSync20(join39(memoryDir, file), "utf-8"));
29984
+ const data = JSON.parse(readFileSync21(join39(memoryDir, file), "utf-8"));
29952
29985
  if (data && typeof data === "object") {
29953
29986
  const keys = Object.keys(data).slice(0, 3);
29954
29987
  for (const key of keys) {
@@ -30083,7 +30116,7 @@ var init_carousel_descriptors = __esm({
30083
30116
  });
30084
30117
 
30085
30118
  // packages/cli/dist/tui/voice.js
30086
- import { existsSync as existsSync30, mkdirSync as mkdirSync11, writeFileSync as writeFileSync11, readFileSync as readFileSync21, unlinkSync as unlinkSync4 } from "node:fs";
30119
+ import { existsSync as existsSync30, mkdirSync as mkdirSync11, writeFileSync as writeFileSync11, readFileSync as readFileSync22, unlinkSync as unlinkSync4 } from "node:fs";
30087
30120
  import { join as join40 } from "node:path";
30088
30121
  import { homedir as homedir12, tmpdir as tmpdir6, platform as platform3 } from "node:os";
30089
30122
  import { execSync as execSync26, spawn as nodeSpawn } from "node:child_process";
@@ -31037,7 +31070,7 @@ var init_voice = __esm({
31037
31070
  };
31038
31071
  if (existsSync30(pkgPath)) {
31039
31072
  try {
31040
- const existing = JSON.parse(readFileSync21(pkgPath, "utf8"));
31073
+ const existing = JSON.parse(readFileSync22(pkgPath, "utf8"));
31041
31074
  if (!existing.dependencies?.["phonemizer"]) {
31042
31075
  existing.dependencies = { ...existing.dependencies, ...expectedDeps };
31043
31076
  writeFileSync11(pkgPath, JSON.stringify(existing, null, 2));
@@ -31181,7 +31214,7 @@ Error: ${err instanceof Error ? err.message : String(err)}`);
31181
31214
  if (!existsSync30(onnxPath) || !existsSync30(configPath)) {
31182
31215
  throw new Error(`Model files not found for ${this.modelId}`);
31183
31216
  }
31184
- this.config = JSON.parse(readFileSync21(configPath, "utf8"));
31217
+ this.config = JSON.parse(readFileSync22(configPath, "utf8"));
31185
31218
  renderInfo("Loading voice model...");
31186
31219
  this.session = await this.ort.InferenceSession.create(onnxPath, {
31187
31220
  executionProviders: ["cpu"],
@@ -32159,7 +32192,7 @@ var init_edit_history = __esm({
32159
32192
  });
32160
32193
 
32161
32194
  // packages/cli/dist/tui/promptLoader.js
32162
- import { readFileSync as readFileSync22, existsSync as existsSync31 } from "node:fs";
32195
+ import { readFileSync as readFileSync23, existsSync as existsSync31 } from "node:fs";
32163
32196
  import { join as join42, dirname as dirname15 } from "node:path";
32164
32197
  import { fileURLToPath as fileURLToPath11 } from "node:url";
32165
32198
  function loadPrompt3(promptPath, vars) {
@@ -32169,7 +32202,7 @@ function loadPrompt3(promptPath, vars) {
32169
32202
  if (!existsSync31(fullPath)) {
32170
32203
  throw new Error(`Prompt file not found: ${fullPath}`);
32171
32204
  }
32172
- content = readFileSync22(fullPath, "utf-8");
32205
+ content = readFileSync23(fullPath, "utf-8");
32173
32206
  cache3.set(promptPath, content);
32174
32207
  }
32175
32208
  if (!vars)
@@ -32190,7 +32223,7 @@ var init_promptLoader3 = __esm({
32190
32223
  });
32191
32224
 
32192
32225
  // packages/cli/dist/tui/dream-engine.js
32193
- import { mkdirSync as mkdirSync13, writeFileSync as writeFileSync12, readFileSync as readFileSync23, existsSync as existsSync32, cpSync, rmSync, readdirSync as readdirSync10 } from "node:fs";
32226
+ import { mkdirSync as mkdirSync13, writeFileSync as writeFileSync12, readFileSync as readFileSync24, existsSync as existsSync32, cpSync, rmSync, readdirSync as readdirSync10 } from "node:fs";
32194
32227
  import { join as join43, basename as basename12 } from "node:path";
32195
32228
  import { execSync as execSync27 } from "node:child_process";
32196
32229
  function loadAutoresearchMemory(repoRoot) {
@@ -32198,7 +32231,7 @@ function loadAutoresearchMemory(repoRoot) {
32198
32231
  if (!existsSync32(memoryPath))
32199
32232
  return "";
32200
32233
  try {
32201
- const raw = readFileSync23(memoryPath, "utf-8");
32234
+ const raw = readFileSync24(memoryPath, "utf-8");
32202
32235
  const data = JSON.parse(raw);
32203
32236
  const sections = [];
32204
32237
  for (const key of AUTORESEARCH_MEMORY_KEYS) {
@@ -32431,7 +32464,7 @@ var init_dream_engine = __esm({
32431
32464
  if (!existsSync32(targetPath)) {
32432
32465
  return { success: false, output: "", error: `File not found: ${rawPath}`, durationMs: Date.now() - start };
32433
32466
  }
32434
- let content = readFileSync23(targetPath, "utf-8");
32467
+ let content = readFileSync24(targetPath, "utf-8");
32435
32468
  if (!content.includes(oldStr)) {
32436
32469
  return { success: false, output: "", error: "old_string not found in file", durationMs: Date.now() - start };
32437
32470
  }
@@ -32520,7 +32553,7 @@ var init_dream_engine = __esm({
32520
32553
  if (!existsSync32(targetPath)) {
32521
32554
  return { success: false, output: "", error: `File not found: ${rawPath}`, durationMs: Date.now() - start };
32522
32555
  }
32523
- let content = readFileSync23(targetPath, "utf-8");
32556
+ let content = readFileSync24(targetPath, "utf-8");
32524
32557
  if (!content.includes(oldStr)) {
32525
32558
  return { success: false, output: "", error: "old_string not found in file", durationMs: Date.now() - start };
32526
32559
  }
@@ -33798,7 +33831,7 @@ var init_bless_engine = __esm({
33798
33831
  });
33799
33832
 
33800
33833
  // packages/cli/dist/tui/dmn-engine.js
33801
- import { existsSync as existsSync33, readFileSync as readFileSync24, writeFileSync as writeFileSync13, mkdirSync as mkdirSync14, readdirSync as readdirSync11, unlinkSync as unlinkSync5 } from "node:fs";
33834
+ import { existsSync as existsSync33, readFileSync as readFileSync25, writeFileSync as writeFileSync13, mkdirSync as mkdirSync14, readdirSync as readdirSync11, unlinkSync as unlinkSync5 } from "node:fs";
33802
33835
  import { join as join44, basename as basename13 } from "node:path";
33803
33836
  function buildDMNGatherPrompt(recentTaskSummaries, dueReminders, attentionItems, memoryTopics, capabilities, competence, reflectionBuffer) {
33804
33837
  const competenceReport = competence.length > 0 ? competence.map((c3) => {
@@ -34526,7 +34559,7 @@ OUTPUT: Call task_complete with JSON:
34526
34559
  const path = join44(this.stateDir, "state.json");
34527
34560
  if (existsSync33(path)) {
34528
34561
  try {
34529
- this.state = JSON.parse(readFileSync24(path, "utf-8"));
34562
+ this.state = JSON.parse(readFileSync25(path, "utf-8"));
34530
34563
  } catch {
34531
34564
  }
34532
34565
  }
@@ -34558,7 +34591,7 @@ OUTPUT: Call task_complete with JSON:
34558
34591
  });
34559
34592
 
34560
34593
  // packages/cli/dist/tui/snr-engine.js
34561
- import { existsSync as existsSync34, readdirSync as readdirSync12, readFileSync as readFileSync25 } from "node:fs";
34594
+ import { existsSync as existsSync34, readdirSync as readdirSync12, readFileSync as readFileSync26 } from "node:fs";
34562
34595
  import { join as join45, basename as basename14 } from "node:path";
34563
34596
  function computeDPrime(signalScores, noiseScores) {
34564
34597
  if (signalScores.length === 0 || noiseScores.length === 0)
@@ -34812,7 +34845,7 @@ Call task_complete with the JSON array when done.`, onEvent)
34812
34845
  if (topics.length > 0 && !topics.includes(topic))
34813
34846
  continue;
34814
34847
  try {
34815
- const data = JSON.parse(readFileSync25(join45(dir, f), "utf-8"));
34848
+ const data = JSON.parse(readFileSync26(join45(dir, f), "utf-8"));
34816
34849
  for (const [key, val] of Object.entries(data)) {
34817
34850
  const value = typeof val === "object" && val !== null && "value" in val ? String(val.value) : String(val);
34818
34851
  entries.push({ topic, key, value });
@@ -35866,7 +35899,8 @@ with summary "no_reply" to silently skip without responding.
35866
35899
  existing.runner.injectUserMessage(msg.text);
35867
35900
  this.tuiWrite(() => renderTelegramSubAgentEvent(msg.username, "mid-conversation steering injected"));
35868
35901
  } else {
35869
- this.tuiWrite(() => renderTelegramSubAgentEvent(msg.username, "queued (sub-agent still starting)"));
35902
+ existing.pendingMessages.push(msg.text);
35903
+ this.tuiWrite(() => renderTelegramSubAgentEvent(msg.username, `queued (${existing.pendingMessages.length} pending)`));
35870
35904
  }
35871
35905
  return;
35872
35906
  }
@@ -35880,7 +35914,8 @@ with summary "no_reply" to silently skip without responding.
35880
35914
  intermediateLines: [],
35881
35915
  lastEditMs: 0,
35882
35916
  aborted: false,
35883
- toolContext
35917
+ toolContext,
35918
+ pendingMessages: []
35884
35919
  };
35885
35920
  this.subAgents.set(msg.chatId, subAgent);
35886
35921
  this.state.activeSubAgents = this.subAgents.size;
@@ -35953,6 +35988,13 @@ with summary "no_reply" to silently skip without responding.
35953
35988
  streamEnabled: true
35954
35989
  });
35955
35990
  subAgent.runner = runner;
35991
+ if (subAgent.pendingMessages.length > 0) {
35992
+ for (const queued of subAgent.pendingMessages) {
35993
+ runner.injectUserMessage(queued);
35994
+ }
35995
+ this.tuiWrite(() => renderTelegramSubAgentEvent(msg.username, `replayed ${subAgent.pendingMessages.length} queued message(s)`));
35996
+ subAgent.pendingMessages.length = 0;
35997
+ }
35956
35998
  const tools = this.buildSubAgentTools(ctx, repoRoot, msg.chatId);
35957
35999
  runner.registerTools(tools);
35958
36000
  runner.onEvent((event) => {
@@ -37845,7 +37887,7 @@ import { cwd } from "node:process";
37845
37887
  import { resolve as resolve28, join as join47, dirname as dirname16, extname as extname9 } from "node:path";
37846
37888
  import { createRequire as createRequire2 } from "node:module";
37847
37889
  import { fileURLToPath as fileURLToPath12 } from "node:url";
37848
- import { readFileSync as readFileSync26, rmSync as rmSync2, readdirSync as readdirSync14 } from "node:fs";
37890
+ import { readFileSync as readFileSync27, rmSync as rmSync2, readdirSync as readdirSync14 } from "node:fs";
37849
37891
  import { existsSync as existsSync36 } from "node:fs";
37850
37892
  function formatTimeAgo(date) {
37851
37893
  const seconds = Math.floor((Date.now() - date.getTime()) / 1e3);
@@ -38077,7 +38119,7 @@ function gatherMemorySnippets(root) {
38077
38119
  continue;
38078
38120
  try {
38079
38121
  for (const f of readdirSync14(dir).filter((f2) => f2.endsWith(".json"))) {
38080
- const data = JSON.parse(readFileSync26(join47(dir, f), "utf-8"));
38122
+ const data = JSON.parse(readFileSync27(join47(dir, f), "utf-8"));
38081
38123
  for (const val of Object.values(data)) {
38082
38124
  const v = typeof val === "object" && val !== null && "value" in val ? String(val.value) : String(val);
38083
38125
  if (v.length > 10)
@@ -39419,6 +39461,11 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
39419
39461
  if (voiceSession?.isActive) {
39420
39462
  return voiceSession.tunnelUrl;
39421
39463
  }
39464
+ if (!voiceEngine.enabled || !voiceEngine.ready) {
39465
+ writeContent(() => renderInfo("Auto-enabling voice for call session..."));
39466
+ const voiceMsg = await voiceEngine.toggle();
39467
+ writeContent(() => renderInfo(voiceMsg));
39468
+ }
39422
39469
  if (!adminSessionKey) {
39423
39470
  adminSessionKey = generateSessionKey();
39424
39471
  }
@@ -39821,10 +39868,24 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
39821
39868
  writeContent(() => {
39822
39869
  renderInfo(`Previous session found (${savedCtx.entries.length} entries, last active ${timeAgo})`);
39823
39870
  renderInfo(`Last task: ${lastTask}${lastEntry.task && lastEntry.task.length > 80 ? "..." : ""}`);
39824
- renderInfo(`Restore previous context? (y/n)`);
39871
+ renderInfo(`Restore previous context? (y/n) [auto-restores in 15s]`);
39825
39872
  });
39826
39873
  showPrompt();
39827
39874
  pendingSessionRestore = true;
39875
+ setTimeout(() => {
39876
+ if (pendingSessionRestore) {
39877
+ pendingSessionRestore = false;
39878
+ const prompt = buildContextRestorePrompt(repoRoot);
39879
+ if (prompt) {
39880
+ restoredSessionContext = prompt;
39881
+ const info = loadSessionContext(repoRoot);
39882
+ writeContent(() => renderInfo(`Context auto-restored from ${info?.entries.length ?? 0} session(s).`));
39883
+ } else {
39884
+ writeContent(() => renderInfo("No context to restore. Starting fresh."));
39885
+ }
39886
+ showPrompt();
39887
+ }
39888
+ }, 15e3);
39828
39889
  }, 150);
39829
39890
  }
39830
39891
  }
@@ -40058,7 +40119,7 @@ Execute this skill now. Follow the behavioral guidance above.`;
40058
40119
  if (isImage) {
40059
40120
  try {
40060
40121
  const imgPath = resolve28(repoRoot, cleanPath);
40061
- const imgBuffer = readFileSync26(imgPath);
40122
+ const imgBuffer = readFileSync27(imgPath);
40062
40123
  const base64 = imgBuffer.toString("base64");
40063
40124
  const ext = extname9(cleanPath).toLowerCase();
40064
40125
  const mime = ext === ".png" ? "image/png" : ext === ".gif" ? "image/gif" : ext === ".webp" ? "image/webp" : "image/jpeg";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "open-agents-ai",
3
- "version": "0.79.0",
3
+ "version": "0.81.0",
4
4
  "description": "AI coding agent powered by open-source models (Ollama/vLLM) — interactive TUI with agentic tool-calling loop",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",