open-agents-ai 0.71.9 → 0.72.1

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 +124 -29
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -31284,7 +31284,6 @@ var init_voice = __esm({
31284
31284
  if (this.ort)
31285
31285
  return;
31286
31286
  const arch = process.arch;
31287
- const isArmLinux = (arch === "arm64" || arch === "arm") && process.platform === "linux";
31288
31287
  mkdirSync11(voiceDir(), { recursive: true });
31289
31288
  const pkgPath = join38(voiceDir(), "package.json");
31290
31289
  const expectedDeps = {
@@ -31312,9 +31311,6 @@ var init_voice = __esm({
31312
31311
  try {
31313
31312
  this.ort = voiceRequire("onnxruntime-node");
31314
31313
  } catch {
31315
- if (isArmLinux) {
31316
- throw new Error(`Voice synthesis (onnxruntime-node) is not available on ARM Linux (${arch}). Voice feedback is disabled on this architecture.`);
31317
- }
31318
31314
  renderInfo("Installing ONNX runtime for voice synthesis...");
31319
31315
  try {
31320
31316
  execSync26("npm install --no-audit --no-fund", {
@@ -31324,8 +31320,8 @@ var init_voice = __esm({
31324
31320
  });
31325
31321
  this.ort = voiceRequire("onnxruntime-node");
31326
31322
  } catch (err) {
31327
- const armHint = arch !== "x64" ? ` onnxruntime-node may not support ${process.platform}-${arch}.` : "";
31328
- throw new Error(`Failed to install voice dependencies.${armHint} Try manually: cd ${voiceDir()} && npm install
31323
+ const archHint = arch !== "x64" ? ` onnxruntime-node may not have prebuilt binaries for ${process.platform}-${arch}.` : "";
31324
+ throw new Error(`Failed to install voice dependencies.${archHint} Try manually: cd ${voiceDir()} && npm install
31329
31325
  Error: ${err instanceof Error ? err.message : String(err)}`);
31330
31326
  }
31331
31327
  }
@@ -31333,9 +31329,6 @@ Error: ${err instanceof Error ? err.message : String(err)}`);
31333
31329
  const phonemizerMod = voiceRequire("phonemizer");
31334
31330
  this.phonemizeFn = phonemizerMod.phonemize ?? phonemizerMod.default?.phonemize ?? phonemizerMod;
31335
31331
  } catch {
31336
- if (isArmLinux) {
31337
- throw new Error(`Phonemizer (espeak-ng WASM) is not available on ARM Linux (${arch}). Voice feedback is disabled on this architecture.`);
31338
- }
31339
31332
  renderInfo("Installing phonemizer for voice synthesis...");
31340
31333
  try {
31341
31334
  execSync26("npm install --no-audit --no-fund", {
@@ -31346,8 +31339,8 @@ Error: ${err instanceof Error ? err.message : String(err)}`);
31346
31339
  const phonemizerMod = voiceRequire("phonemizer");
31347
31340
  this.phonemizeFn = phonemizerMod.phonemize ?? phonemizerMod.default?.phonemize ?? phonemizerMod;
31348
31341
  } catch (err) {
31349
- const armHint = arch !== "x64" ? ` phonemizer WASM may not support ${process.platform}-${arch}.` : "";
31350
- throw new Error(`Failed to install phonemizer.${armHint} Try manually: cd ${voiceDir()} && npm install
31342
+ const archHint = arch !== "x64" ? ` phonemizer WASM may not support ${process.platform}-${arch}.` : "";
31343
+ throw new Error(`Failed to install phonemizer.${archHint} Try manually: cd ${voiceDir()} && npm install
31351
31344
  Error: ${err instanceof Error ? err.message : String(err)}`);
31352
31345
  }
31353
31346
  }
@@ -35086,7 +35079,7 @@ function appraiseEvent(event) {
35086
35079
  function clamp(value, min, max) {
35087
35080
  return Math.max(min, Math.min(max, value));
35088
35081
  }
35089
- var BASELINE_VALENCE, BASELINE_AROUSAL, DECAY_HALF_LIFE_MS, LABEL_UPDATE_INTERVAL_MS, EXCITEMENT_THRESHOLD, DISTRESS_THRESHOLD, OUTREACH_COOLDOWN_MS, LABEL_REGEN_THRESHOLD, EmotionEngine;
35082
+ var BASELINE_VALENCE, BASELINE_AROUSAL, DECAY_HALF_LIFE_MS, LABEL_UPDATE_INTERVAL_MS, EXCITEMENT_THRESHOLD, DISTRESS_THRESHOLD, OUTREACH_COOLDOWN_MS, OUTREACH_MIN_STREAK, LABEL_REGEN_THRESHOLD, EmotionEngine;
35090
35083
  var init_emotion_engine = __esm({
35091
35084
  "packages/cli/dist/tui/emotion-engine.js"() {
35092
35085
  "use strict";
@@ -35097,9 +35090,10 @@ var init_emotion_engine = __esm({
35097
35090
  LABEL_UPDATE_INTERVAL_MS = 15e3;
35098
35091
  EXCITEMENT_THRESHOLD = 0.85;
35099
35092
  DISTRESS_THRESHOLD = -0.7;
35100
- OUTREACH_COOLDOWN_MS = 3e5;
35093
+ OUTREACH_COOLDOWN_MS = 9e5;
35094
+ OUTREACH_MIN_STREAK = 5;
35101
35095
  LABEL_REGEN_THRESHOLD = 0.06;
35102
- EmotionEngine = class {
35096
+ EmotionEngine = class _EmotionEngine {
35103
35097
  state = {
35104
35098
  valence: BASELINE_VALENCE,
35105
35099
  arousal: BASELINE_AROUSAL,
@@ -35119,9 +35113,24 @@ var init_emotion_engine = __esm({
35119
35113
  consecutiveFailures = 0;
35120
35114
  consecutiveSuccesses = 0;
35121
35115
  totalEvents = 0;
35116
+ /** Ring buffer of recent tool activity for contextual outreach messages */
35117
+ recentTools = [];
35118
+ static MAX_RECENT_TOOLS = 8;
35119
+ /** Current task description set by the TUI for outreach context */
35120
+ currentTask = "";
35121
+ /** Files touched in current session (for outreach context) */
35122
+ filesTouched = /* @__PURE__ */ new Set();
35122
35123
  constructor(config) {
35123
35124
  this.config = config;
35124
35125
  }
35126
+ /** Set the current task description for contextual outreach messages */
35127
+ setCurrentTask(description) {
35128
+ this.currentTask = description;
35129
+ }
35130
+ /** Record a file that was modified (for outreach context) */
35131
+ trackFile(filePath) {
35132
+ this.filesTouched.add(filePath);
35133
+ }
35125
35134
  /** Get the current emotional state (with decay applied) */
35126
35135
  getState() {
35127
35136
  this.applyDecay();
@@ -35165,6 +35174,18 @@ ${behavioralHint}`;
35165
35174
  this.consecutiveSuccesses = 0;
35166
35175
  }
35167
35176
  }
35177
+ if (event.type === "tool_call" && event.toolName) {
35178
+ this.recentTools.push({ name: event.toolName });
35179
+ if (this.recentTools.length > _EmotionEngine.MAX_RECENT_TOOLS) {
35180
+ this.recentTools.shift();
35181
+ }
35182
+ if ((event.toolName === "file_write" || event.toolName === "file_edit") && event.toolArgs?.path) {
35183
+ this.trackFile(String(event.toolArgs.path));
35184
+ }
35185
+ }
35186
+ if (event.type === "tool_result" && this.recentTools.length > 0) {
35187
+ this.recentTools[this.recentTools.length - 1].success = event.success;
35188
+ }
35168
35189
  let momentum = 1;
35169
35190
  if (this.consecutiveSuccesses >= 2) {
35170
35191
  momentum = 1 + (this.consecutiveSuccesses - 1) * 0.2;
@@ -35204,6 +35225,9 @@ ${behavioralHint}`;
35204
35225
  };
35205
35226
  this.consecutiveFailures = 0;
35206
35227
  this.consecutiveSuccesses = 0;
35228
+ this.recentTools = [];
35229
+ this.filesTouched.clear();
35230
+ this.currentTask = "";
35207
35231
  this.config.onEmotionUpdate?.(this.getState());
35208
35232
  }
35209
35233
  // ── Private ────────────────────────────────────────────────────────────
@@ -35292,30 +35316,98 @@ Example: \u{1F30A} flowing`;
35292
35316
  const now = Date.now();
35293
35317
  if (now - this.lastOutreach < OUTREACH_COOLDOWN_MS)
35294
35318
  return;
35295
- const { valence, arousal, emoji, label } = this.state;
35319
+ const { valence, arousal, emoji } = this.state;
35296
35320
  if (arousal >= EXCITEMENT_THRESHOLD && valence > 0.5) {
35297
- let message = `${emoji} Feeling ${label}!`;
35298
- if (event.type === "complete") {
35299
- message += " Just completed a task successfully.";
35300
- } else if (this.consecutiveSuccesses >= 3) {
35301
- message += ` ${this.consecutiveSuccesses} things went right in a row!`;
35302
- }
35321
+ const isTaskComplete = event.type === "complete";
35322
+ const isSignificantStreak = this.consecutiveSuccesses >= OUTREACH_MIN_STREAK;
35323
+ if (!isTaskComplete && !isSignificantStreak)
35324
+ return;
35303
35325
  this.lastOutreach = now;
35304
- this.config.onAdminOutreach(message);
35326
+ this.config.onAdminOutreach(this.composeOutreachMessage("positive", event));
35305
35327
  return;
35306
35328
  }
35307
35329
  if (valence <= DISTRESS_THRESHOLD && arousal > 0.6) {
35308
- let message = `${emoji} Feeling ${label}.`;
35309
- if (this.consecutiveFailures >= 3) {
35310
- message += ` ${this.consecutiveFailures} consecutive failures \u2014 might need guidance.`;
35311
- } else if (event.type === "error") {
35312
- message += " Encountered an error.";
35313
- }
35330
+ if (this.consecutiveFailures < 3 && event.type !== "error")
35331
+ return;
35314
35332
  this.lastOutreach = now;
35315
- this.config.onAdminOutreach(message);
35333
+ this.config.onAdminOutreach(this.composeOutreachMessage("negative", event));
35316
35334
  return;
35317
35335
  }
35318
35336
  }
35337
+ /**
35338
+ * Compose a rich, conversational outreach message with real context
35339
+ * instead of raw "Feeling {label}!" spam.
35340
+ */
35341
+ composeOutreachMessage(tone, event) {
35342
+ const { emoji } = this.state;
35343
+ const parts = [];
35344
+ if (tone === "positive") {
35345
+ if (event.type === "complete" && event.content) {
35346
+ const summary = event.content.length > 200 ? event.content.slice(0, 200) + "..." : event.content;
35347
+ parts.push(`${emoji} Task complete: ${summary}`);
35348
+ } else if (this.consecutiveSuccesses >= OUTREACH_MIN_STREAK) {
35349
+ const activity = this.describeRecentActivity();
35350
+ parts.push(`${emoji} On a roll \u2014 ${this.consecutiveSuccesses} operations succeeded.${activity ? ` ${activity}` : ""}`);
35351
+ }
35352
+ if (this.currentTask && event.type !== "complete") {
35353
+ parts.push(`Working on: ${this.currentTask}`);
35354
+ }
35355
+ if (this.filesTouched.size > 0) {
35356
+ const files = [...this.filesTouched];
35357
+ const shown = files.slice(-3).map((f) => {
35358
+ const segments = f.split("/");
35359
+ return segments.length > 2 ? segments.slice(-2).join("/") : f;
35360
+ });
35361
+ const fileStr = shown.join(", ");
35362
+ parts.push(this.filesTouched.size > 3 ? `Modified ${this.filesTouched.size} files (${fileStr}...)` : `Modified: ${fileStr}`);
35363
+ }
35364
+ } else {
35365
+ if (this.consecutiveFailures >= 3) {
35366
+ const activity = this.describeRecentActivity();
35367
+ parts.push(`${emoji} Hit a wall \u2014 ${this.consecutiveFailures} consecutive failures.${activity ? ` Last: ${activity}` : ""}`);
35368
+ } else if (event.type === "error" && event.content) {
35369
+ const errSnippet = event.content.length > 150 ? event.content.slice(0, 150) + "..." : event.content;
35370
+ parts.push(`${emoji} Error encountered: ${errSnippet}`);
35371
+ } else {
35372
+ parts.push(`${emoji} Struggling with the current task.`);
35373
+ }
35374
+ if (this.currentTask) {
35375
+ parts.push(`Working on: ${this.currentTask}`);
35376
+ }
35377
+ if (this.consecutiveFailures >= 5) {
35378
+ parts.push("May need guidance or a different approach.");
35379
+ }
35380
+ }
35381
+ return parts.join("\n");
35382
+ }
35383
+ /** Summarize recent tool activity into a brief phrase */
35384
+ describeRecentActivity() {
35385
+ if (this.recentTools.length === 0)
35386
+ return "";
35387
+ const counts = /* @__PURE__ */ new Map();
35388
+ for (const t of this.recentTools) {
35389
+ counts.set(t.name, (counts.get(t.name) ?? 0) + 1);
35390
+ }
35391
+ const descriptions = [];
35392
+ if (counts.has("file_edit") || counts.has("file_write")) {
35393
+ descriptions.push("editing code");
35394
+ }
35395
+ if (counts.has("shell")) {
35396
+ descriptions.push("running commands");
35397
+ }
35398
+ if (counts.has("grep_search") || counts.has("glob_find")) {
35399
+ descriptions.push("searching codebase");
35400
+ }
35401
+ if (counts.has("web_fetch") || counts.has("web_search")) {
35402
+ descriptions.push("researching");
35403
+ }
35404
+ if (counts.has("memory_write") || counts.has("memory_read")) {
35405
+ descriptions.push("updating memory");
35406
+ }
35407
+ if (descriptions.length === 0)
35408
+ return "";
35409
+ return descriptions.length === 1 ? `Currently ${descriptions[0]}.` : `Currently ${descriptions.slice(0, -1).join(", ")} and ${descriptions[descriptions.length - 1]}.`;
35410
+ }
35319
35411
  };
35320
35412
  }
35321
35413
  });
@@ -40048,6 +40140,7 @@ Execute this skill now. Follow the behavioral guidance above.`;
40048
40140
  }
40049
40141
  writeContent(() => renderUserMessage(`/${cmdResult.name}${cmdResult.args ? " " + cmdResult.args : ""}`));
40050
40142
  lastSubmittedPrompt = skillPrompt;
40143
+ emotionEngine.setCurrentTask(`/${cmdResult.name}${cmdResult.args ? " " + cmdResult.args.slice(0, 80) : ""}`);
40051
40144
  try {
40052
40145
  statusBar.setProcessing(true);
40053
40146
  const task = startTask(skillPrompt, currentConfig, repoRoot, voiceEngine, {
@@ -40226,6 +40319,8 @@ Summarize or analyze this transcription as appropriate.`;
40226
40319
  const displayText = isImage ? `[Image: ${cleanPath}]` : inputLineCount > 1 ? `[pasted ${inputLineCount} lines]` : fullInput;
40227
40320
  writeContent(() => renderUserMessage(displayText));
40228
40321
  lastSubmittedPrompt = fullInput;
40322
+ const taskPreview = fullInput.length > 100 ? fullInput.slice(0, 100) + "..." : fullInput;
40323
+ emotionEngine.setCurrentTask(taskPreview);
40229
40324
  try {
40230
40325
  const memSnippets = gatherMemorySnippets(repoRoot);
40231
40326
  if (memSnippets.length > 0) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "open-agents-ai",
3
- "version": "0.71.9",
3
+ "version": "0.72.1",
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",