open-agents-ai 0.138.81 → 0.138.82

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 +516 -42
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -22304,6 +22304,10 @@ var init_agenticRunner = __esm({
22304
22304
  get taskState() {
22305
22305
  return this._taskState;
22306
22306
  }
22307
+ /** Get the backend (for post-task LLM extraction calls) */
22308
+ getBackend() {
22309
+ return this.backend;
22310
+ }
22307
22311
  /** Get the file state registry (for external inspection) */
22308
22312
  get fileRegistry() {
22309
22313
  return this._fileRegistry;
@@ -43885,7 +43889,7 @@ async function handlePeerEndpoint(peerId, authKey, ctx, local) {
43885
43889
  }
43886
43890
  }
43887
43891
  async function handleParallel(arg, ctx) {
43888
- const { execSync: execSync30 } = await import("node:child_process");
43892
+ const { execSync: execSync31 } = await import("node:child_process");
43889
43893
  const baseUrl = ctx.config.backendUrl || "http://localhost:11434";
43890
43894
  const isRemote = ctx.config.backendType === "nexus";
43891
43895
  if (isRemote) {
@@ -43909,7 +43913,7 @@ async function handleParallel(arg, ctx) {
43909
43913
  }
43910
43914
  let systemdVal = "";
43911
43915
  try {
43912
- const out = execSync30("systemctl show ollama.service -p Environment 2>/dev/null || true", { encoding: "utf8" });
43916
+ const out = execSync31("systemctl show ollama.service -p Environment 2>/dev/null || true", { encoding: "utf8" });
43913
43917
  const match = out.match(/OLLAMA_NUM_PARALLEL=(\d+)/);
43914
43918
  if (match)
43915
43919
  systemdVal = match[1];
@@ -43938,7 +43942,7 @@ async function handleParallel(arg, ctx) {
43938
43942
  }
43939
43943
  const isSystemd = (() => {
43940
43944
  try {
43941
- const out = execSync30("systemctl is-active ollama.service 2>/dev/null", { encoding: "utf8" }).trim();
43945
+ const out = execSync31("systemctl is-active ollama.service 2>/dev/null", { encoding: "utf8" }).trim();
43942
43946
  return out === "active" || out === "inactive";
43943
43947
  } catch {
43944
43948
  return false;
@@ -43952,10 +43956,10 @@ async function handleParallel(arg, ctx) {
43952
43956
  const overrideContent = `[Service]
43953
43957
  Environment="OLLAMA_NUM_PARALLEL=${n}"
43954
43958
  `;
43955
- execSync30(`sudo mkdir -p ${overrideDir}`, { stdio: "pipe" });
43956
- execSync30(`echo '${overrideContent}' | sudo tee ${overrideFile} > /dev/null`, { stdio: "pipe" });
43957
- execSync30("sudo systemctl daemon-reload", { stdio: "pipe" });
43958
- execSync30("sudo systemctl restart ollama.service", { stdio: "pipe" });
43959
+ execSync31(`sudo mkdir -p ${overrideDir}`, { stdio: "pipe" });
43960
+ execSync31(`echo '${overrideContent}' | sudo tee ${overrideFile} > /dev/null`, { stdio: "pipe" });
43961
+ execSync31("sudo systemctl daemon-reload", { stdio: "pipe" });
43962
+ execSync31("sudo systemctl restart ollama.service", { stdio: "pipe" });
43959
43963
  let ready = false;
43960
43964
  for (let i = 0; i < 30 && !ready; i++) {
43961
43965
  await new Promise((r) => setTimeout(r, 500));
@@ -43982,7 +43986,7 @@ Environment="OLLAMA_NUM_PARALLEL=${n}"
43982
43986
  renderInfo(`Setting OLLAMA_NUM_PARALLEL=${n}...`);
43983
43987
  try {
43984
43988
  try {
43985
- execSync30("pkill -f 'ollama serve' 2>/dev/null || true", { stdio: "pipe" });
43989
+ execSync31("pkill -f 'ollama serve' 2>/dev/null || true", { stdio: "pipe" });
43986
43990
  } catch {
43987
43991
  }
43988
43992
  await new Promise((r) => setTimeout(r, 1e3));
@@ -44734,18 +44738,18 @@ async function showExposeDashboard(gateway, rl, ctx) {
44734
44738
  const cmd = `/endpoint ${id} --auth ${gateway.authKey ?? ""}`;
44735
44739
  let copied = false;
44736
44740
  try {
44737
- const { execSync: execSync30 } = __require("node:child_process");
44741
+ const { execSync: execSync31 } = __require("node:child_process");
44738
44742
  const platform5 = process.platform;
44739
44743
  if (platform5 === "darwin") {
44740
- execSync30("pbcopy", { input: cmd, timeout: 3e3 });
44744
+ execSync31("pbcopy", { input: cmd, timeout: 3e3 });
44741
44745
  copied = true;
44742
44746
  } else if (platform5 === "win32") {
44743
- execSync30("clip", { input: cmd, timeout: 3e3 });
44747
+ execSync31("clip", { input: cmd, timeout: 3e3 });
44744
44748
  copied = true;
44745
44749
  } else {
44746
44750
  for (const tool of ["xclip -selection clipboard", "xsel --clipboard --input", "wl-copy"]) {
44747
44751
  try {
44748
- execSync30(tool, { input: cmd, timeout: 3e3, stdio: ["pipe", "pipe", "pipe"] });
44752
+ execSync31(tool, { input: cmd, timeout: 3e3, stdio: ["pipe", "pipe", "pipe"] });
44749
44753
  copied = true;
44750
44754
  break;
44751
44755
  } catch {
@@ -52183,15 +52187,301 @@ var init_system_metrics = __esm({
52183
52187
  }
52184
52188
  });
52185
52189
 
52190
+ // packages/cli/dist/tui/text-selection.js
52191
+ import { execSync as execSync29 } from "node:child_process";
52192
+ function stripAnsi3(s) {
52193
+ return s.replace(/\x1B\[[0-9;]*[A-Za-z]|\x1B\].*?(?:\x07|\x1B\\)/g, "");
52194
+ }
52195
+ function visibleLength(s) {
52196
+ return stripAnsi3(s).length;
52197
+ }
52198
+ function copyText(text) {
52199
+ try {
52200
+ const platform5 = process.platform;
52201
+ if (platform5 === "darwin") {
52202
+ execSync29("pbcopy", { input: text, timeout: 3e3 });
52203
+ return true;
52204
+ }
52205
+ if (platform5 === "win32") {
52206
+ execSync29("clip", { input: text, timeout: 3e3 });
52207
+ return true;
52208
+ }
52209
+ for (const tool of ["xclip -selection clipboard", "xsel --clipboard --input", "wl-copy"]) {
52210
+ try {
52211
+ execSync29(tool, { input: text, timeout: 3e3 });
52212
+ return true;
52213
+ } catch {
52214
+ continue;
52215
+ }
52216
+ }
52217
+ } catch {
52218
+ }
52219
+ try {
52220
+ const b64 = Buffer.from(text).toString("base64");
52221
+ process.stdout.write(`\x1B]52;c;${b64}\x07`);
52222
+ return true;
52223
+ } catch {
52224
+ }
52225
+ return false;
52226
+ }
52227
+ function computeHeaderButtons(termWidth) {
52228
+ const buttons = ["/help", "/voice", "/cohere", "/model"];
52229
+ const GAP = 2;
52230
+ const result = [];
52231
+ let col = termWidth - 1;
52232
+ for (let i = buttons.length - 1; i >= 0; i--) {
52233
+ const label = ` ${buttons[i]} `;
52234
+ const endCol = col;
52235
+ const startCol = col - label.length + 1;
52236
+ if (startCol < 1)
52237
+ break;
52238
+ result.unshift({
52239
+ label,
52240
+ command: buttons[i],
52241
+ startCol,
52242
+ endCol,
52243
+ row: 3
52244
+ });
52245
+ col = startCol - GAP - 1;
52246
+ }
52247
+ return result;
52248
+ }
52249
+ function renderHeaderButtons(termWidth) {
52250
+ const buttons = computeHeaderButtons(termWidth);
52251
+ if (buttons.length === 0)
52252
+ return "";
52253
+ let out = "";
52254
+ for (const btn of buttons) {
52255
+ out += `\x1B[${btn.row};${btn.startCol}H`;
52256
+ out += `\x1B[1;${SEL_FG}m\x1B[48;5;${SEL_BG}m${btn.label}\x1B[0m`;
52257
+ }
52258
+ return out;
52259
+ }
52260
+ function hitTestHeaderButton(row, col, termWidth) {
52261
+ const buttons = computeHeaderButtons(termWidth);
52262
+ for (const btn of buttons) {
52263
+ if (row === btn.row && col >= btn.startCol && col <= btn.endCol) {
52264
+ return btn.command;
52265
+ }
52266
+ }
52267
+ return null;
52268
+ }
52269
+ var SEL_BG, SEL_FG, SEL_START, SEL_END, TextSelection;
52270
+ var init_text_selection = __esm({
52271
+ "packages/cli/dist/tui/text-selection.js"() {
52272
+ "use strict";
52273
+ SEL_BG = 178;
52274
+ SEL_FG = 30;
52275
+ SEL_START = `\x1B[${SEL_FG}m\x1B[48;5;${SEL_BG}m`;
52276
+ SEL_END = `\x1B[0m`;
52277
+ TextSelection = class {
52278
+ _selection = null;
52279
+ _active = false;
52280
+ // true while mouse button is held
52281
+ _blockModeArmed = false;
52282
+ // Ctrl+Shift+B pressed, next click starts block select
52283
+ _provider;
52284
+ constructor(provider) {
52285
+ this._provider = provider;
52286
+ }
52287
+ /** Whether a selection currently exists */
52288
+ get hasSelection() {
52289
+ return this._selection !== null;
52290
+ }
52291
+ /** Whether we're actively dragging */
52292
+ get isDragging() {
52293
+ return this._active;
52294
+ }
52295
+ /** Get the current selection range (or null) */
52296
+ get selection() {
52297
+ return this._selection;
52298
+ }
52299
+ /** Arm block selection mode — next click starts rectangular select */
52300
+ armBlockMode() {
52301
+ this._blockModeArmed = true;
52302
+ }
52303
+ /** Clear the current selection */
52304
+ clear() {
52305
+ this._selection = null;
52306
+ this._active = false;
52307
+ this._blockModeArmed = false;
52308
+ }
52309
+ /**
52310
+ * Handle mouse press (button 0, M suffix in SGR).
52311
+ * Starts a new selection from the click position.
52312
+ */
52313
+ onMousePress(row, col) {
52314
+ const mode = this._blockModeArmed ? "block" : "line";
52315
+ this._blockModeArmed = false;
52316
+ this._selection = {
52317
+ anchor: { row, col },
52318
+ current: { row, col },
52319
+ mode
52320
+ };
52321
+ this._active = true;
52322
+ }
52323
+ /**
52324
+ * Handle mouse drag (button 32, M suffix in SGR).
52325
+ * Extends the selection to the current cursor position.
52326
+ */
52327
+ onMouseDrag(row, col) {
52328
+ if (!this._active || !this._selection)
52329
+ return;
52330
+ this._selection.current = { row, col };
52331
+ }
52332
+ /**
52333
+ * Handle mouse release (button 0, m suffix in SGR).
52334
+ * Finalizes the selection.
52335
+ */
52336
+ onMouseRelease(row, col) {
52337
+ if (!this._active || !this._selection)
52338
+ return;
52339
+ this._selection.current = { row, col };
52340
+ this._active = false;
52341
+ if (this._selection.anchor.row === this._selection.current.row && this._selection.anchor.col === this._selection.current.col) {
52342
+ this._selection = null;
52343
+ }
52344
+ }
52345
+ /**
52346
+ * Compute which content buffer indices and column ranges are selected.
52347
+ * Returns an array of { bufferIdx, startCol, endCol } for each selected line.
52348
+ * Columns are 0-based visible character positions.
52349
+ */
52350
+ getSelectedRanges() {
52351
+ if (!this._selection)
52352
+ return [];
52353
+ const { anchor, current, mode } = this._selection;
52354
+ const top = this._provider.getScrollRegionTop();
52355
+ const height = this._provider.getContentHeight();
52356
+ const offset = this._provider.getScrollOffset();
52357
+ const totalLines = this._provider.getContentLines().length;
52358
+ const startIdx = Math.max(0, totalLines - height - offset);
52359
+ const anchorBufIdx = startIdx + (anchor.row - top);
52360
+ const currentBufIdx = startIdx + (current.row - top);
52361
+ const minRow = Math.min(anchorBufIdx, currentBufIdx);
52362
+ const maxRow = Math.max(anchorBufIdx, currentBufIdx);
52363
+ const minCol = Math.min(anchor.col, current.col);
52364
+ const maxCol = Math.max(anchor.col, current.col);
52365
+ const ranges = [];
52366
+ if (mode === "block") {
52367
+ for (let idx = minRow; idx <= maxRow; idx++) {
52368
+ if (idx >= 0 && idx < totalLines) {
52369
+ ranges.push({ bufferIdx: idx, startCol: minCol - 1, endCol: maxCol - 1 });
52370
+ }
52371
+ }
52372
+ } else {
52373
+ const isForward = anchorBufIdx < currentBufIdx || anchorBufIdx === currentBufIdx && anchor.col <= current.col;
52374
+ const startR = isForward ? anchorBufIdx : currentBufIdx;
52375
+ const endR = isForward ? currentBufIdx : anchorBufIdx;
52376
+ const startC = isForward ? anchor.col - 1 : current.col - 1;
52377
+ const endC = isForward ? current.col - 1 : anchor.col - 1;
52378
+ for (let idx = startR; idx <= endR; idx++) {
52379
+ if (idx < 0 || idx >= totalLines)
52380
+ continue;
52381
+ const lineLen = visibleLength(this._provider.getContentLines()[idx] ?? "");
52382
+ if (idx === startR && idx === endR) {
52383
+ ranges.push({ bufferIdx: idx, startCol: startC, endCol: endC });
52384
+ } else if (idx === startR) {
52385
+ ranges.push({ bufferIdx: idx, startCol: startC, endCol: Math.max(lineLen, startC) });
52386
+ } else if (idx === endR) {
52387
+ ranges.push({ bufferIdx: idx, startCol: 0, endCol: endC });
52388
+ } else {
52389
+ ranges.push({ bufferIdx: idx, startCol: 0, endCol: lineLen });
52390
+ }
52391
+ }
52392
+ }
52393
+ return ranges;
52394
+ }
52395
+ /**
52396
+ * Apply selection highlighting to a content line for rendering.
52397
+ * Takes the original ANSI line and returns it with selection highlight applied.
52398
+ *
52399
+ * @param line Original content line (with ANSI codes)
52400
+ * @param startCol 0-based visible start column to highlight
52401
+ * @param endCol 0-based visible end column to highlight (inclusive)
52402
+ * @returns Line with selection highlight overlay
52403
+ */
52404
+ static applyHighlight(line, startCol, endCol) {
52405
+ const plain = stripAnsi3(line);
52406
+ if (startCol > plain.length || endCol < 0 || startCol > endCol)
52407
+ return line;
52408
+ const sc = Math.max(0, startCol);
52409
+ const ec = Math.min(plain.length - 1, endCol);
52410
+ let result = "";
52411
+ let visPos = 0;
52412
+ let i = 0;
52413
+ let inHighlight = false;
52414
+ while (i < line.length) {
52415
+ const escMatch = line.slice(i).match(/^(\x1B\[[0-9;]*[A-Za-z]|\x1B\].*?(?:\x07|\x1B\\))/);
52416
+ if (escMatch) {
52417
+ if (inHighlight) {
52418
+ result += SEL_END + escMatch[0] + SEL_START;
52419
+ } else {
52420
+ result += escMatch[0];
52421
+ }
52422
+ i += escMatch[0].length;
52423
+ continue;
52424
+ }
52425
+ if (visPos === sc && !inHighlight) {
52426
+ result += SEL_START;
52427
+ inHighlight = true;
52428
+ }
52429
+ result += line[i];
52430
+ if (visPos === ec && inHighlight) {
52431
+ result += SEL_END;
52432
+ inHighlight = false;
52433
+ }
52434
+ visPos++;
52435
+ i++;
52436
+ }
52437
+ if (inHighlight)
52438
+ result += SEL_END;
52439
+ return result;
52440
+ }
52441
+ /**
52442
+ * Get the selected text content (plain text, no ANSI codes).
52443
+ * For block mode, each line is joined with newline.
52444
+ * For line mode, text flows continuously with newlines between lines.
52445
+ */
52446
+ getSelectedText() {
52447
+ const ranges = this.getSelectedRanges();
52448
+ if (ranges.length === 0)
52449
+ return "";
52450
+ const lines = this._provider.getContentLines();
52451
+ const parts = [];
52452
+ for (const { bufferIdx, startCol, endCol } of ranges) {
52453
+ const raw = lines[bufferIdx] ?? "";
52454
+ const plain = stripAnsi3(raw);
52455
+ const selected = plain.slice(Math.max(0, startCol), Math.min(plain.length, endCol + 1));
52456
+ parts.push(selected);
52457
+ }
52458
+ return parts.join("\n");
52459
+ }
52460
+ /**
52461
+ * Copy current selection to system clipboard.
52462
+ * Tries platform commands first, falls back to OSC 52.
52463
+ * Returns true if copy succeeded.
52464
+ */
52465
+ copyToClipboard() {
52466
+ const text = this.getSelectedText();
52467
+ if (!text)
52468
+ return false;
52469
+ return copyText(text);
52470
+ }
52471
+ };
52472
+ }
52473
+ });
52474
+
52186
52475
  // packages/cli/dist/tui/status-bar.js
52187
52476
  import { readFileSync as readFileSync31 } from "node:fs";
52188
- var EXPERT_TOOL_BASELINES, CONTEXT_SWITCH_OVERHEAD, TURN_PLANNING_OVERHEAD, DEFAULT_TOOL_BASELINE, CODE_READ_CHARS_PER_SEC, PROSE_READ_CHARS_PER_SEC, MIN_CONTENT_FOR_READING, CODE_CONTENT_TOOLS, PROSE_CONTENT_TOOLS, HumanSpeedTracker, PANEL_BG, CONTENT_BG, PANEL_BG_SEQ, CONTENT_BG_SEQ, RESET, StatusBar;
52477
+ var EXPERT_TOOL_BASELINES, CONTEXT_SWITCH_OVERHEAD, TURN_PLANNING_OVERHEAD, DEFAULT_TOOL_BASELINE, CODE_READ_CHARS_PER_SEC, PROSE_READ_CHARS_PER_SEC, MIN_CONTENT_FOR_READING, CODE_CONTENT_TOOLS, PROSE_CONTENT_TOOLS, HumanSpeedTracker, PANEL_BG, CONTENT_BG, TEXT_PRIMARY, PANEL_BG_SEQ, CONTENT_BG_SEQ, RESET, StatusBar;
52189
52478
  var init_status_bar = __esm({
52190
52479
  "packages/cli/dist/tui/status-bar.js"() {
52191
52480
  "use strict";
52192
52481
  init_render();
52193
52482
  init_braille_spinner();
52194
52483
  init_system_metrics();
52484
+ init_text_selection();
52195
52485
  EXPERT_TOOL_BASELINES = {
52196
52486
  file_read: 12,
52197
52487
  structured_read: 15,
@@ -52351,6 +52641,7 @@ var init_status_bar = __esm({
52351
52641
  };
52352
52642
  PANEL_BG = 234;
52353
52643
  CONTENT_BG = 233;
52644
+ TEXT_PRIMARY = 178;
52354
52645
  PANEL_BG_SEQ = `\x1B[48;5;${PANEL_BG}m`;
52355
52646
  CONTENT_BG_SEQ = `\x1B[48;5;${CONTENT_BG}m`;
52356
52647
  RESET = "\x1B[0m";
@@ -52378,6 +52669,15 @@ var init_status_bar = __esm({
52378
52669
  _mouseIdleTimer = null;
52379
52670
  static MOUSE_IDLE_MS = 1500;
52380
52671
  // disable after 1.5s idle
52672
+ /** Text selection state — tracks click-drag selection for copy */
52673
+ _textSelection = new TextSelection({
52674
+ getContentLines: () => this._contentLines,
52675
+ getScrollOffset: () => this._contentScrollOffset,
52676
+ getContentHeight: () => this.contentHeight,
52677
+ getScrollRegionTop: () => this.scrollRegionTop
52678
+ });
52679
+ /** Callback for header button clicks — fires slash commands */
52680
+ _headerButtonHandler = null;
52381
52681
  /** Banner refresh callback — called after resize to re-render top rows */
52382
52682
  _bannerRefresh = null;
52383
52683
  /** Track previous terminal dimensions to clear ghost separators on resize */
@@ -53021,6 +53321,91 @@ var init_status_bar = __esm({
53021
53321
  this._mouseIdleTimer = null;
53022
53322
  }
53023
53323
  }
53324
+ // ── Text Selection + Header Buttons ─────────────────────────────────
53325
+ /** Set the handler for header button clicks (dispatches slash commands) */
53326
+ setHeaderButtonHandler(handler) {
53327
+ this._headerButtonHandler = handler;
53328
+ }
53329
+ /** Get the text selection instance (for external keyboard shortcut wiring) */
53330
+ get textSelection() {
53331
+ return this._textSelection;
53332
+ }
53333
+ /**
53334
+ * Handle a mouse pointer event (press/drag/release).
53335
+ * Called by MouseFilterStream's pointer handler.
53336
+ * Routes to: header button clicks, text selection, or ignored.
53337
+ */
53338
+ handlePointerEvent(type, col, row) {
53339
+ if (!this.active)
53340
+ return;
53341
+ if (type === "press" && row < this.scrollRegionTop) {
53342
+ const w = process.stdout.columns ?? 80;
53343
+ const cmd = hitTestHeaderButton(row, col, w);
53344
+ if (cmd && this._headerButtonHandler) {
53345
+ this._headerButtonHandler(cmd);
53346
+ return;
53347
+ }
53348
+ }
53349
+ if (row < this.scrollRegionTop)
53350
+ return;
53351
+ const rows = process.stdout.rows ?? 24;
53352
+ const fh = this._currentFooterHeight;
53353
+ const footerStart = rows - fh + 1;
53354
+ if (row >= footerStart)
53355
+ return;
53356
+ if (type === "press") {
53357
+ this._textSelection.clear();
53358
+ this._textSelection.onMousePress(row, col);
53359
+ this.repaintContent();
53360
+ } else if (type === "drag") {
53361
+ this._textSelection.onMouseDrag(row, col);
53362
+ if (this._textSelection.isDragging)
53363
+ this.repaintContent();
53364
+ } else if (type === "release") {
53365
+ this._textSelection.onMouseRelease(row, col);
53366
+ this.repaintContent();
53367
+ }
53368
+ }
53369
+ /** Copy current selection to clipboard. Returns true if copied. */
53370
+ copySelection() {
53371
+ if (!this._textSelection.hasSelection)
53372
+ return false;
53373
+ const ok = this._textSelection.copyToClipboard();
53374
+ if (ok) {
53375
+ const rows = process.stdout.rows ?? 24;
53376
+ const pos = this.rowPositions(rows);
53377
+ const writer = this._origWrite ?? process.stdout.write.bind(process.stdout);
53378
+ writer(`\x1B[${pos.metricsRow};1H\x1B[2K\x1B[38;5;${TEXT_PRIMARY}m \u2713 Copied to clipboard\x1B[0m`);
53379
+ setTimeout(() => {
53380
+ if (this.active)
53381
+ this.renderFooterAndPositionInput();
53382
+ }, 1200);
53383
+ }
53384
+ return ok;
53385
+ }
53386
+ /** Arm block (rectangular) selection mode — next click starts block select */
53387
+ armBlockSelection() {
53388
+ this._textSelection.armBlockMode();
53389
+ const rows = process.stdout.rows ?? 24;
53390
+ const pos = this.rowPositions(rows);
53391
+ const writer = this._origWrite ?? process.stdout.write.bind(process.stdout);
53392
+ writer(`\x1B[${pos.metricsRow};1H\x1B[2K\x1B[38;5;${TEXT_PRIMARY}m \u25A0 Block selection mode \u2014 click and drag\x1B[0m`);
53393
+ setTimeout(() => {
53394
+ if (this.active)
53395
+ this.renderFooterAndPositionInput();
53396
+ }, 1500);
53397
+ }
53398
+ /** Render header buttons overlay on banner row 3 */
53399
+ renderHeaderButtons() {
53400
+ if (!this.active)
53401
+ return;
53402
+ const w = process.stdout.columns ?? 80;
53403
+ const overlay = renderHeaderButtons(w);
53404
+ if (overlay) {
53405
+ const writer = this._origWrite ?? process.stdout.write.bind(process.stdout);
53406
+ writer(overlay);
53407
+ }
53408
+ }
53024
53409
  /** Set a callback to re-render the banner after resize/redraw */
53025
53410
  setBannerRefresh(refresh) {
53026
53411
  this._bannerRefresh = refresh;
@@ -53215,6 +53600,7 @@ ${CONTENT_BG_SEQ}`);
53215
53600
  scrollContentUp(lines = 1) {
53216
53601
  if (!this.active)
53217
53602
  return;
53603
+ this._textSelection.clear();
53218
53604
  this.cancelMouseIdle();
53219
53605
  this.enableMouseTracking();
53220
53606
  const maxOffset = Math.max(0, this._contentLines.length - this.contentHeight);
@@ -53226,6 +53612,7 @@ ${CONTENT_BG_SEQ}`);
53226
53612
  scrollContentDown(lines = 1) {
53227
53613
  if (!this.active)
53228
53614
  return;
53615
+ this._textSelection.clear();
53229
53616
  this.cancelMouseIdle();
53230
53617
  this.enableMouseTracking();
53231
53618
  this._contentScrollOffset = Math.max(0, this._contentScrollOffset - lines);
@@ -53262,10 +53649,18 @@ ${CONTENT_BG_SEQ}`);
53262
53649
  let buf = "\x1B[?2026h";
53263
53650
  buf += "\x1B7";
53264
53651
  buf += "\x1B[?25l";
53652
+ const selRanges = this._textSelection.hasSelection ? this._textSelection.getSelectedRanges() : [];
53653
+ const selByBuf = /* @__PURE__ */ new Map();
53654
+ for (const r of selRanges)
53655
+ selByBuf.set(r.bufferIdx, r);
53265
53656
  for (let row = 0; row < h; row++) {
53266
53657
  const lineIdx = startIdx + row;
53267
- const line = lineIdx < totalLines ? this._contentLines[lineIdx] : "";
53658
+ let line = lineIdx < totalLines ? this._contentLines[lineIdx] : "";
53268
53659
  const screenRow = this.scrollRegionTop + row;
53660
+ const sel = selByBuf.get(lineIdx);
53661
+ if (sel) {
53662
+ line = TextSelection.applyHighlight(line, sel.startCol, sel.endCol);
53663
+ }
53269
53664
  buf += `\x1B[${screenRow};1H${CONTENT_BG_SEQ}\x1B[2K${line}`;
53270
53665
  }
53271
53666
  if (this._contentScrollOffset > 0) {
@@ -53917,6 +54312,22 @@ ${CONTENT_BG_SEQ}`);
53917
54312
  onCtrlO();
53918
54313
  return;
53919
54314
  }
54315
+ if (key?.ctrl && key?.shift && key?.name === "c") {
54316
+ self.copySelection();
54317
+ return;
54318
+ }
54319
+ if (s === "\x1B[67;6u") {
54320
+ self.copySelection();
54321
+ return;
54322
+ }
54323
+ if (key?.ctrl && key?.shift && key?.name === "b") {
54324
+ self.armBlockSelection();
54325
+ return;
54326
+ }
54327
+ if (s === "\x1B[66;6u") {
54328
+ self.armBlockSelection();
54329
+ return;
54330
+ }
53920
54331
  if (s && (/\x1B\[</.test(s) || /^\d+;\d+;\d*[Mm]/.test(s) || /^;\d+[Mm]/.test(s) || /\[<\d/.test(s))) {
53921
54332
  return;
53922
54333
  }
@@ -54040,11 +54451,13 @@ var init_mouse_filter = __esm({
54040
54451
  buffer = "";
54041
54452
  onScroll = null;
54042
54453
  onActivity = null;
54454
+ onPointer = null;
54043
54455
  flushTimer = null;
54044
- constructor(scrollHandler, activityHandler) {
54456
+ constructor(scrollHandler, activityHandler, pointerHandler) {
54045
54457
  super();
54046
54458
  this.onScroll = scrollHandler;
54047
54459
  this.onActivity = activityHandler ?? null;
54460
+ this.onPointer = pointerHandler ?? null;
54048
54461
  }
54049
54462
  _transform(chunk, _encoding, callback) {
54050
54463
  this.buffer += chunk.toString();
@@ -54060,10 +54473,20 @@ var init_mouse_filter = __esm({
54060
54473
  if (mouseMatch) {
54061
54474
  const btn = parseInt(mouseMatch[1]);
54062
54475
  const row = parseInt(mouseMatch[3]);
54476
+ const col = parseInt(mouseMatch[2]);
54477
+ const suffix = mouseMatch[4];
54063
54478
  if ((btn === 64 || btn === 96) && this.onScroll)
54064
54479
  this.onScroll("up", 3, row);
54065
54480
  else if ((btn === 65 || btn === 97) && this.onScroll)
54066
54481
  this.onScroll("down", 3, row);
54482
+ else if (this.onPointer) {
54483
+ if (btn === 0 && suffix === "M")
54484
+ this.onPointer("press", col, row);
54485
+ else if (btn === 32 && suffix === "M")
54486
+ this.onPointer("drag", col, row);
54487
+ else if (btn === 0 && suffix === "m")
54488
+ this.onPointer("release", col, row);
54489
+ }
54067
54490
  if (this.onActivity)
54068
54491
  this.onActivity();
54069
54492
  i += mouseMatch[0].length;
@@ -54122,7 +54545,7 @@ import { createRequire as createRequire2 } from "node:module";
54122
54545
  import { fileURLToPath as fileURLToPath12 } from "node:url";
54123
54546
  import { readFileSync as readFileSync32, writeFileSync as writeFileSync18, appendFileSync as appendFileSync4, rmSync as rmSync2, readdirSync as readdirSync16, mkdirSync as mkdirSync19 } from "node:fs";
54124
54547
  import { existsSync as existsSync43 } from "node:fs";
54125
- import { execSync as execSync29 } from "node:child_process";
54548
+ import { execSync as execSync30 } from "node:child_process";
54126
54549
  import { homedir as homedir13 } from "node:os";
54127
54550
  function formatTimeAgo(date) {
54128
54551
  const seconds = Math.floor((Date.now() - date.getTime()) / 1e3);
@@ -55518,7 +55941,10 @@ async function startInteractive(config, repoPath) {
55518
55941
  if (process.stdout.isTTY) {
55519
55942
  const scrollTop = carouselLines > 0 ? carouselLines : 1;
55520
55943
  statusBar.activate(scrollTop);
55521
- statusBar.setBannerRefresh(() => banner.renderCurrentFrame());
55944
+ statusBar.setBannerRefresh(() => {
55945
+ banner.renderCurrentFrame();
55946
+ statusBar.renderHeaderButtons();
55947
+ });
55522
55948
  const { onOverlayLeave: onOverlayLeave2 } = await Promise.resolve().then(() => (init_overlay_lock(), overlay_lock_exports));
55523
55949
  onOverlayLeave2(() => {
55524
55950
  banner.renderCurrentFrame();
@@ -55528,6 +55954,7 @@ async function startInteractive(config, repoPath) {
55528
55954
  if (cohereEnabled) {
55529
55955
  statusBar.setCohereActive(true);
55530
55956
  }
55957
+ statusBar.renderHeaderButtons();
55531
55958
  if (isResumed) {
55532
55959
  statusBar.beginContentWrite();
55533
55960
  const resumeMsg = hasTaskToResume ? `Updated to v${version} \u2014 picking up where you left off.` : `Updated to v${version}.`;
@@ -55907,6 +56334,8 @@ Rationale: ${proposal.rationale}${provenanceNote}`;
55907
56334
  statusBar.cancelMouseIdle();
55908
56335
  statusBar.enableMouseTracking();
55909
56336
  statusBar.scheduleMouseIdle();
56337
+ }, (type, col, row) => {
56338
+ statusBar.handlePointerEvent(type, col, row);
55910
56339
  });
55911
56340
  process.stdin.pipe(mouseFilter);
55912
56341
  const rl = readline2.createInterface({
@@ -55929,6 +56358,21 @@ Rationale: ${proposal.rationale}${provenanceNote}`;
55929
56358
  statusBar.handleResize();
55930
56359
  }
55931
56360
  });
56361
+ let commandCtxRef = null;
56362
+ statusBar.setHeaderButtonHandler((command) => {
56363
+ if (!commandCtxRef)
56364
+ return;
56365
+ (async () => {
56366
+ try {
56367
+ if (statusBar.isActive)
56368
+ statusBar.beginContentWrite();
56369
+ await handleSlashCommand(command, commandCtxRef);
56370
+ if (statusBar.isActive)
56371
+ statusBar.endContentWrite();
56372
+ } catch {
56373
+ }
56374
+ })();
56375
+ });
55932
56376
  rl.output = null;
55933
56377
  if (process.stdin.isTTY && typeof process.stdin.setRawMode === "function") {
55934
56378
  process.stdin.setRawMode(true);
@@ -57318,7 +57762,7 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
57318
57762
  try {
57319
57763
  if (process.platform === "win32") {
57320
57764
  try {
57321
- execSync29(`taskkill /F /PID ${pid}`, { timeout: 5e3, stdio: "ignore" });
57765
+ execSync30(`taskkill /F /PID ${pid}`, { timeout: 5e3, stdio: "ignore" });
57322
57766
  } catch {
57323
57767
  }
57324
57768
  } else {
@@ -57345,7 +57789,7 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
57345
57789
  if (pid > 0) {
57346
57790
  if (process.platform === "win32") {
57347
57791
  try {
57348
- execSync29(`taskkill /F /PID ${pid}`, { timeout: 5e3, stdio: "ignore" });
57792
+ execSync30(`taskkill /F /PID ${pid}`, { timeout: 5e3, stdio: "ignore" });
57349
57793
  } catch {
57350
57794
  }
57351
57795
  } else {
@@ -57362,7 +57806,7 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
57362
57806
  } catch {
57363
57807
  }
57364
57808
  try {
57365
- execSync29(process.platform === "win32" ? "timeout /t 1 /nobreak >nul" : "sleep 0.5", { timeout: 3e3, stdio: "ignore" });
57809
+ execSync30(process.platform === "win32" ? "timeout /t 1 /nobreak >nul" : "sleep 0.5", { timeout: 3e3, stdio: "ignore" });
57366
57810
  } catch {
57367
57811
  }
57368
57812
  const oaPath = join59(repoRoot, OA_DIR);
@@ -57376,14 +57820,14 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
57376
57820
  } catch (err) {
57377
57821
  if (attempt < 2) {
57378
57822
  try {
57379
- execSync29(process.platform === "win32" ? "timeout /t 1 /nobreak >nul" : "sleep 0.3", { timeout: 3e3, stdio: "ignore" });
57823
+ execSync30(process.platform === "win32" ? "timeout /t 1 /nobreak >nul" : "sleep 0.3", { timeout: 3e3, stdio: "ignore" });
57380
57824
  } catch {
57381
57825
  }
57382
57826
  } else {
57383
57827
  writeContent(() => renderWarning(`Could not fully remove ${OA_DIR}/: ${err instanceof Error ? err.message : String(err)}`));
57384
57828
  if (process.platform === "win32") {
57385
57829
  try {
57386
- execSync29(`rd /s /q "${oaPath}"`, { timeout: 1e4, stdio: "ignore" });
57830
+ execSync30(`rd /s /q "${oaPath}"`, { timeout: 1e4, stdio: "ignore" });
57387
57831
  deleted = true;
57388
57832
  } catch {
57389
57833
  }
@@ -57432,6 +57876,7 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
57432
57876
  return { entries: ctx.entries.length, lastSaved: ctx.updatedAt };
57433
57877
  }
57434
57878
  };
57879
+ commandCtxRef = commandCtx;
57435
57880
  showPrompt();
57436
57881
  if (!isResumed) {
57437
57882
  const savedCtx = loadSessionContext(repoRoot);
@@ -58323,19 +58768,48 @@ async function runWithTUI(task, config, repoPath) {
58323
58768
  try {
58324
58769
  const ts = handle.runner?.taskState;
58325
58770
  if (ts && ts.toolCallCount > 2) {
58326
- let category = "strategy";
58327
- let lesson = "";
58328
- if (ts.failedApproaches && ts.failedApproaches.length > 0) {
58329
- category = "recovery";
58330
- const failed = ts.failedApproaches.slice(0, 2).join("; ");
58331
- const worked = ts.completedSteps?.slice(-2).join(" \u2192 ") || "completed task";
58332
- lesson = `[recovery] Avoid: ${failed}. Instead: ${worked}`;
58333
- } else if (ts.completedSteps && ts.completedSteps.length > 1) {
58334
- lesson = `[strategy] ${ts.completedSteps.slice(-3).join(" \u2192 ")}`;
58335
- } else {
58336
- lesson = `[strategy] Solved: ${task.slice(0, 150)}`;
58337
- }
58338
- if (lesson) {
58771
+ const extractionBackend = handle.runner.getBackend();
58772
+ const modFiles = Array.from(ts.modifiedFiles.entries()).map(([p, a]) => `${p} (${a})`).join(", ");
58773
+ const trajectoryPrompt = `You are a memory extraction system. Given this completed task trajectory, extract ONE concise reusable lesson.
58774
+
58775
+ TASK: ${task.slice(0, 300)}
58776
+ COMPLETED STEPS: ${(ts.completedSteps || []).slice(-5).join("; ")}
58777
+ FAILED APPROACHES: ${(ts.failedApproaches || []).join("; ") || "none"}
58778
+ FILES MODIFIED: ${modFiles || "none"}
58779
+ TOOL CALLS: ${ts.toolCallCount}
58780
+
58781
+ Extract the lesson in EXACTLY this format (no other text):
58782
+ CATEGORY: strategy|recovery|optimization
58783
+ TRIGGER: <what problem signature activates this \u2014 one line>
58784
+ LESSON: <one-sentence generalizable rule \u2014 the actual fix pattern, NOT error messages>
58785
+ STEPS: <ordered concrete steps, semicolon-separated>
58786
+
58787
+ Rules:
58788
+ - LESSON must describe the FIX PATTERN (e.g. "use ?? null after Map.get()"), never the error text
58789
+ - Keep total response under 200 characters for LESSON field
58790
+ - Use descriptive variable names for non-fixed elements (e.g. "the target file" not "/src/foo.ts")
58791
+ - If FAILED APPROACHES exist, category MUST be "recovery"`;
58792
+ const extractionResp = await extractionBackend.chatCompletion({
58793
+ messages: [
58794
+ { role: "system", content: "You extract structured procedural memories from task trajectories. Respond ONLY in the requested format." },
58795
+ { role: "user", content: trajectoryPrompt }
58796
+ ],
58797
+ tools: [],
58798
+ temperature: 0,
58799
+ maxTokens: 300,
58800
+ timeoutMs: 3e4
58801
+ });
58802
+ const raw = extractionResp.choices?.[0]?.message?.content || "";
58803
+ const categoryMatch = raw.match(/CATEGORY:\s*(strategy|recovery|optimization)/i);
58804
+ const triggerMatch = raw.match(/TRIGGER:\s*(.+)/i);
58805
+ const lessonMatch = raw.match(/LESSON:\s*(.+)/i);
58806
+ const stepsMatch = raw.match(/STEPS:\s*(.+)/i);
58807
+ const category = categoryMatch?.[1]?.toLowerCase() || "strategy";
58808
+ const trigger = triggerMatch?.[1]?.trim().slice(0, 150) || "";
58809
+ const lesson = lessonMatch?.[1]?.trim().slice(0, 200) || "";
58810
+ const steps = stepsMatch?.[1]?.trim().slice(0, 300) || "";
58811
+ if (lesson && lesson.length > 5) {
58812
+ const content = `[${category}] TRIGGER: ${trigger} | LESSON: ${lesson}` + (steps ? ` | STEPS: ${steps}` : "");
58339
58813
  const metaDir = join59(repoRoot, ".oa", "memory", "metabolism");
58340
58814
  const storeFile = join59(metaDir, "store.json");
58341
58815
  let store = [];
@@ -58347,15 +58821,15 @@ async function runWithTUI(task, config, repoPath) {
58347
58821
  store.push({
58348
58822
  id: `mem-traj-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 6)}`,
58349
58823
  type: "procedural",
58350
- content: lesson.slice(0, 500),
58351
- sourceTrace: "trajectory-extraction",
58824
+ content: content.slice(0, 600),
58825
+ sourceTrace: "llm-trajectory-extraction",
58352
58826
  scores: {
58353
- novelty: 0.6,
58354
- utility: category === "recovery" ? 0.8 : 0.6,
58355
- confidence: 0.7,
58827
+ novelty: 0.7,
58828
+ utility: category === "recovery" ? 0.85 : 0.7,
58829
+ confidence: 0.8,
58356
58830
  identityRelevance: 0.3
58357
58831
  },
58358
- decision: { action: "admit", reason: `Auto-extracted ${category} from task (${ts.toolCallCount} tool calls)` },
58832
+ decision: { action: "admit", reason: `LLM-extracted ${category} from task (${ts.toolCallCount} tool calls)` },
58359
58833
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
58360
58834
  lastAccessedAt: (/* @__PURE__ */ new Date()).toISOString(),
58361
58835
  accessCount: 0
@@ -58382,7 +58856,7 @@ async function runWithTUI(task, config, repoPath) {
58382
58856
  const metaFile = join59(repoRoot, ".oa", "memory", "metabolism", "store.json");
58383
58857
  if (existsSync43(metaFile)) {
58384
58858
  const store = JSON.parse(readFileSync32(metaFile, "utf8"));
58385
- const latest = store.filter((m) => m.sourceTrace === "trajectory-extraction").slice(-1)[0];
58859
+ const latest = store.filter((m) => m.sourceTrace === "trajectory-extraction" || m.sourceTrace === "llm-trajectory-extraction").slice(-1)[0];
58386
58860
  if (latest && latest.scores?.confidence >= 0.6) {
58387
58861
  try {
58388
58862
  const { NexusTool: NexusTool2 } = __require("@open-agents/execution");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "open-agents-ai",
3
- "version": "0.138.81",
3
+ "version": "0.138.82",
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",