open-agents-ai 0.20.0 → 0.20.2

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 +173 -12
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -15075,16 +15075,15 @@ async function handleUpdate(subcommand, ctx) {
15075
15075
  process.stdout.write(` ${c2.green("\u2714")} Installed v${info.latestVersion}. Reloading...
15076
15076
 
15077
15077
  `);
15078
- if (ctx.savePendingTaskState) {
15079
- ctx.savePendingTaskState();
15080
- }
15078
+ const hadActiveTask = ctx.savePendingTaskState?.() ?? false;
15079
+ const resumeFlag = hadActiveTask ? "1" : "update-only";
15081
15080
  const { execPath, argv } = process;
15082
15081
  try {
15083
15082
  const { execFileSync } = await import("node:child_process");
15084
- process.env.__OA_RESUMED = "1";
15083
+ process.env.__OA_RESUMED = resumeFlag;
15085
15084
  execFileSync(execPath, argv.slice(1), {
15086
15085
  stdio: "inherit",
15087
- env: { ...process.env, __OA_RESUMED: "1" }
15086
+ env: { ...process.env, __OA_RESUMED: resumeFlag }
15088
15087
  });
15089
15088
  } catch {
15090
15089
  renderWarning("Reload failed. Restart oa manually to use the new version.");
@@ -18006,12 +18005,122 @@ ${files.map((f) => `- [\`${f}\`](./${f})`).join("\n")}
18006
18005
  }
18007
18006
  });
18008
18007
 
18008
+ // packages/cli/dist/tui/braille-spinner.js
18009
+ var DENSITY, WAVE, COLOR_RAMP, BrailleSpinner;
18010
+ var init_braille_spinner = __esm({
18011
+ "packages/cli/dist/tui/braille-spinner.js"() {
18012
+ "use strict";
18013
+ DENSITY = [
18014
+ "\u2800",
18015
+ // ⠀ empty
18016
+ "\u2840",
18017
+ // ⡀ dot 7
18018
+ "\u28C0",
18019
+ // ⣀ dots 7,8
18020
+ "\u28C4",
18021
+ // ⣄ dots 3,7,8
18022
+ "\u28E4",
18023
+ // ⣤ dots 3,6,7,8
18024
+ "\u28E6",
18025
+ // ⣦ dots 2,3,6,7,8
18026
+ "\u28F6",
18027
+ // ⣶ dots 2,3,5,6,7,8
18028
+ "\u28F7",
18029
+ // ⣷ dots 1,2,3,5,6,7,8
18030
+ "\u28FF"
18031
+ // ⣿ all dots
18032
+ ];
18033
+ WAVE = [...DENSITY, ...DENSITY.slice(1, -1).reverse()];
18034
+ COLOR_RAMP = [
18035
+ 237,
18036
+ // ⠀ very dim gray
18037
+ 60,
18038
+ // ⡀ muted lavender
18039
+ 97,
18040
+ // ⣀ dim purple
18041
+ 97,
18042
+ // ⣄ dim purple
18043
+ 141,
18044
+ // ⣤ medium purple
18045
+ 141,
18046
+ // ⣦ medium purple
18047
+ 183,
18048
+ // ⣶ light lavender
18049
+ 183,
18050
+ // ⣷ light lavender
18051
+ 189,
18052
+ // ⣿ bright lavender
18053
+ 183,
18054
+ // ⣷ (descending)
18055
+ 183,
18056
+ // ⣶
18057
+ 141,
18058
+ // ⣦
18059
+ 141,
18060
+ // ⣤
18061
+ 97,
18062
+ // ⣄
18063
+ 97,
18064
+ // ⣀
18065
+ 60
18066
+ // ⡀
18067
+ ];
18068
+ BrailleSpinner = class {
18069
+ frame = 0;
18070
+ timer = null;
18071
+ /** Start the animation, calling `onFrame` every tick (~80 ms). */
18072
+ start(onFrame) {
18073
+ this.frame = 0;
18074
+ this.timer = setInterval(() => {
18075
+ this.frame++;
18076
+ onFrame();
18077
+ }, 80);
18078
+ }
18079
+ /** Stop the animation and reset. */
18080
+ stop() {
18081
+ if (this.timer) {
18082
+ clearInterval(this.timer);
18083
+ this.timer = null;
18084
+ }
18085
+ this.frame = 0;
18086
+ }
18087
+ /** Whether the animation timer is active. */
18088
+ get isRunning() {
18089
+ return this.timer !== null;
18090
+ }
18091
+ /**
18092
+ * Render the current animation frame as an ANSI-colored string.
18093
+ * Each column gets a braille character whose density and color vary
18094
+ * sinusoidally, shifted by `this.frame` to create movement.
18095
+ */
18096
+ render(width) {
18097
+ const cycleLen = WAVE.length;
18098
+ let buf = "";
18099
+ let lastColor = -1;
18100
+ for (let col = 0; col < width; col++) {
18101
+ const idx = (col * 2 + this.frame) % cycleLen;
18102
+ const ch = WAVE[idx];
18103
+ const color = COLOR_RAMP[idx];
18104
+ if (color !== lastColor) {
18105
+ buf += `\x1B[38;5;${color}m`;
18106
+ lastColor = color;
18107
+ }
18108
+ buf += ch;
18109
+ }
18110
+ buf += "\x1B[0m";
18111
+ return buf;
18112
+ }
18113
+ };
18114
+ }
18115
+ });
18116
+
18009
18117
  // packages/cli/dist/tui/status-bar.js
18010
18118
  var FOOTER_ROWS, StatusBar;
18011
18119
  var init_status_bar = __esm({
18012
18120
  "packages/cli/dist/tui/status-bar.js"() {
18013
18121
  "use strict";
18014
18122
  init_render();
18123
+ init_braille_spinner();
18015
18124
  FOOTER_ROWS = 5;
18016
18125
  StatusBar = class {
18017
18126
  metrics = {
@@ -18050,6 +18159,9 @@ var init_status_bar = __esm({
18050
18159
  _countdown = 0;
18051
18160
  _recBlink = true;
18052
18161
  _recBlinkTimer = null;
18162
+ /** Whether agent is actively processing (braille animation) */
18163
+ _processing = false;
18164
+ _brailleSpinner = new BrailleSpinner();
18053
18165
  /**
18054
18166
  * Provide a callback that returns readline's current input state.
18055
18167
  * StatusBar uses this to render typed text and position the cursor
@@ -18085,6 +18197,22 @@ var init_status_bar = __esm({
18085
18197
  if (this.active)
18086
18198
  this.renderFooterPreserveCursor();
18087
18199
  }
18200
+ /** Start or stop the braille-dot processing animation on the buffer line. */
18201
+ setProcessing(active) {
18202
+ if (active === this._processing)
18203
+ return;
18204
+ this._processing = active;
18205
+ if (active) {
18206
+ this._brailleSpinner.start(() => {
18207
+ if (this.active)
18208
+ this.renderBufferLine();
18209
+ });
18210
+ } else {
18211
+ this._brailleSpinner.stop();
18212
+ if (this.active)
18213
+ this.renderBufferLine();
18214
+ }
18215
+ }
18088
18216
  /** Context window size to display. Can be updated if model changes. */
18089
18217
  setContextWindowSize(size) {
18090
18218
  this.metrics.contextWindowSize = size;
@@ -18162,7 +18290,7 @@ var init_status_bar = __esm({
18162
18290
  const w = getTermWidth();
18163
18291
  const sep = c2.dim("\u2500".repeat(w));
18164
18292
  const inputRow = rows - 2;
18165
- process.stdout.write(`\x1B[${this.scrollRegionTop};${scrollEnd}r\x1B[?25l\x1B[?7l\x1B[${rows - 4};1H\x1B[2K\x1B[${rows - 3};1H\x1B[2K${sep}\x1B[${inputRow};1H\x1B[2K${this.promptText}\x1B[${rows - 1};1H\x1B[2K${sep}\x1B[${rows};1H\x1B[2K${this.buildMetricsLine()}\x1B[?7h\x1B[${scrollEnd};1H`);
18293
+ process.stdout.write(`\x1B[${this.scrollRegionTop};${scrollEnd}r\x1B[?25l\x1B[?7l\x1B[${rows - 4};1H\x1B[2K${this.buildBufferContent(w)}\x1B[${rows - 3};1H\x1B[2K${sep}\x1B[${inputRow};1H\x1B[2K${this.promptText}\x1B[${rows - 1};1H\x1B[2K${sep}\x1B[${rows};1H\x1B[2K${this.buildMetricsLine()}\x1B[?7h\x1B[${scrollEnd};1H`);
18166
18294
  } else {
18167
18295
  this.applyScrollRegion();
18168
18296
  this.renderFooterAndPositionInput();
@@ -18289,7 +18417,7 @@ var init_status_bar = __esm({
18289
18417
  visibleText = fullLine.slice(scrollOffset, scrollOffset + availWidth);
18290
18418
  cursorCol = this.promptWidth + (cursorPos - scrollOffset) + 1;
18291
18419
  }
18292
- const buf = `\x1B[?7l\x1B[${rows - 4};1H\x1B[2K\x1B[${rows - 3};1H\x1B[2K${sep}\x1B[${inputRow};1H\x1B[2K${this.promptText}${visibleText}\x1B[${rows - 1};1H\x1B[2K${sep}\x1B[${rows};1H\x1B[2K${this.buildMetricsLine()}\x1B[?7h\x1B[?25h\x1B[${inputRow};${cursorCol}H`;
18420
+ const buf = `\x1B[?7l\x1B[${rows - 4};1H\x1B[2K${this.buildBufferContent(w)}\x1B[${rows - 3};1H\x1B[2K${sep}\x1B[${inputRow};1H\x1B[2K${this.promptText}${visibleText}\x1B[${rows - 1};1H\x1B[2K${sep}\x1B[${rows};1H\x1B[2K${this.buildMetricsLine()}\x1B[?7h\x1B[?25h\x1B[${inputRow};${cursorCol}H`;
18293
18421
  process.stdout.write(buf);
18294
18422
  }
18295
18423
  /**
@@ -18308,7 +18436,7 @@ var init_status_bar = __esm({
18308
18436
  const rows = process.stdout.rows ?? 24;
18309
18437
  const w = getTermWidth();
18310
18438
  const sep = c2.dim("\u2500".repeat(w));
18311
- const buf = `\x1B7\x1B[?7l\x1B[${rows - 4};1H\x1B[2K\x1B[${rows - 3};1H\x1B[2K${sep}\x1B[${rows - 1};1H\x1B[2K${sep}\x1B[${rows};1H\x1B[2K${this.buildMetricsLine()}\x1B[?7h\x1B8`;
18439
+ const buf = `\x1B7\x1B[?7l\x1B[${rows - 4};1H\x1B[2K${this.buildBufferContent(w)}\x1B[${rows - 3};1H\x1B[2K${sep}\x1B[${rows - 1};1H\x1B[2K${sep}\x1B[${rows};1H\x1B[2K${this.buildMetricsLine()}\x1B[?7h\x1B8`;
18312
18440
  process.stdout.write(buf);
18313
18441
  }
18314
18442
  /**
@@ -18340,6 +18468,29 @@ var init_status_bar = __esm({
18340
18468
  }
18341
18469
  process.stdout.write(`\x1B7\x1B[?7l\x1B[${inputRow};1H\x1B[2K${this.promptText}${visibleText}\x1B[?7h\x1B8`);
18342
18470
  }
18471
+ /**
18472
+ * Build the content for the buffer line (rows − 4).
18473
+ * Returns braille animation when processing, empty string when idle.
18474
+ */
18475
+ buildBufferContent(width) {
18476
+ if (this._processing && this._brailleSpinner.isRunning) {
18477
+ return this._brailleSpinner.render(width);
18478
+ }
18479
+ return "";
18480
+ }
18481
+ /**
18482
+ * Render ONLY the buffer line (rows − 4) using DEC save/restore cursor.
18483
+ * Called by the braille spinner timer without disrupting scroll or input.
18484
+ */
18485
+ renderBufferLine() {
18486
+ if (!this.active)
18487
+ return;
18488
+ const rows = process.stdout.rows ?? 24;
18489
+ const w = getTermWidth();
18490
+ const bufferRow = rows - 4;
18491
+ const content = this.buildBufferContent(w);
18492
+ process.stdout.write(`\x1B7\x1B[?7l\x1B[${bufferRow};1H\x1B[2K${content}\x1B[?7h\x1B8`);
18493
+ }
18343
18494
  /**
18344
18495
  * Hook into process.stdin to redraw footer after every keystroke.
18345
18496
  * Since readline's output is suppressed (redirected to a no-op stream),
@@ -18752,7 +18903,9 @@ function startTask(task, config, repoRoot, voice, stream, taskStores, bruteForce
18752
18903
  }
18753
18904
  async function startInteractive(config, repoPath) {
18754
18905
  const repoRoot = resolve16(repoPath ?? cwd());
18755
- const isResumed = !!process.env.__OA_RESUMED;
18906
+ const resumeFlag = process.env.__OA_RESUMED ?? "";
18907
+ const isResumed = resumeFlag !== "";
18908
+ const hasTaskToResume = resumeFlag === "1";
18756
18909
  if (isResumed) {
18757
18910
  delete process.env.__OA_RESUMED;
18758
18911
  }
@@ -18842,8 +18995,10 @@ async function startInteractive(config, repoPath) {
18842
18995
  let carouselLines = 0;
18843
18996
  const version = getVersion();
18844
18997
  if (isResumed) {
18845
- renderInfo(`Updated to v${version} \u2014 picking up where you left off.
18846
- `);
18998
+ const resumeMsg = hasTaskToResume ? `Updated to v${version} \u2014 picking up where you left off.
18999
+ ` : `Updated to v${version}.
19000
+ `;
19001
+ renderInfo(resumeMsg);
18847
19002
  } else {
18848
19003
  process.stdout.write("\x1B[2J\x1B[H");
18849
19004
  carouselLines = carousel.start();
@@ -19062,7 +19217,9 @@ async function startInteractive(config, repoPath) {
19062
19217
  savedAt: (/* @__PURE__ */ new Date()).toISOString(),
19063
19218
  toolCallCount: sessionToolCallCount
19064
19219
  });
19220
+ return true;
19065
19221
  }
19222
+ return false;
19066
19223
  },
19067
19224
  dreamStart(mode) {
19068
19225
  if (activeTask) {
@@ -19155,7 +19312,7 @@ async function startInteractive(config, repoPath) {
19155
19312
  setColors: (enabled) => setColorsEnabled(enabled)
19156
19313
  };
19157
19314
  showPrompt();
19158
- if (isResumed) {
19315
+ if (hasTaskToResume) {
19159
19316
  const pendingTask = loadPendingTask(repoRoot);
19160
19317
  if (pendingTask) {
19161
19318
  setTimeout(() => {
@@ -19272,6 +19429,7 @@ Execute this skill now. Follow the behavioral guidance above.`;
19272
19429
  writeContent(() => renderUserMessage(`/${cmdResult.name}${cmdResult.args ? " " + cmdResult.args : ""}`));
19273
19430
  lastSubmittedPrompt = skillPrompt;
19274
19431
  try {
19432
+ statusBar.setProcessing(true);
19275
19433
  const task = startTask(skillPrompt, currentConfig, repoRoot, voiceEngine, {
19276
19434
  enabled: streamEnabled,
19277
19435
  renderer: streamRenderer
@@ -19290,6 +19448,7 @@ Execute this skill now. Follow the behavioral guidance above.`;
19290
19448
  const errMsg = err instanceof Error ? err.message : String(err);
19291
19449
  writeContent(() => renderError(errMsg));
19292
19450
  }
19451
+ statusBar.setProcessing(false);
19293
19452
  activeTask = null;
19294
19453
  showPrompt();
19295
19454
  return;
@@ -19372,6 +19531,7 @@ Summarize or analyze this transcription as appropriate.`;
19372
19531
  writeContent(() => renderUserMessage(displayText));
19373
19532
  lastSubmittedPrompt = fullInput;
19374
19533
  try {
19534
+ statusBar.setProcessing(true);
19375
19535
  const task = startTask(fullInput, currentConfig, repoRoot, voiceEngine, {
19376
19536
  enabled: streamEnabled,
19377
19537
  renderer: streamRenderer
@@ -19409,6 +19569,7 @@ Summarize or analyze this transcription as appropriate.`;
19409
19569
  }
19410
19570
  }
19411
19571
  } finally {
19572
+ statusBar.setProcessing(false);
19412
19573
  if (activeTask) {
19413
19574
  sessionFilesTouched = Array.from(activeTask.filesTouched);
19414
19575
  sessionToolCallCount = activeTask.toolCallCount;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "open-agents-ai",
3
- "version": "0.20.0",
3
+ "version": "0.20.2",
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",