open-agents-ai 0.138.81 → 0.138.83

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 +517 -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,19 @@ ${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
+ }
53664
+ line = line.replace(/\x1B\[0m/g, `\x1B[0m${CONTENT_BG_SEQ}`);
53269
53665
  buf += `\x1B[${screenRow};1H${CONTENT_BG_SEQ}\x1B[2K${line}`;
53270
53666
  }
53271
53667
  if (this._contentScrollOffset > 0) {
@@ -53917,6 +54313,22 @@ ${CONTENT_BG_SEQ}`);
53917
54313
  onCtrlO();
53918
54314
  return;
53919
54315
  }
54316
+ if (key?.ctrl && key?.shift && key?.name === "c") {
54317
+ self.copySelection();
54318
+ return;
54319
+ }
54320
+ if (s === "\x1B[67;6u") {
54321
+ self.copySelection();
54322
+ return;
54323
+ }
54324
+ if (key?.ctrl && key?.shift && key?.name === "b") {
54325
+ self.armBlockSelection();
54326
+ return;
54327
+ }
54328
+ if (s === "\x1B[66;6u") {
54329
+ self.armBlockSelection();
54330
+ return;
54331
+ }
53920
54332
  if (s && (/\x1B\[</.test(s) || /^\d+;\d+;\d*[Mm]/.test(s) || /^;\d+[Mm]/.test(s) || /\[<\d/.test(s))) {
53921
54333
  return;
53922
54334
  }
@@ -54040,11 +54452,13 @@ var init_mouse_filter = __esm({
54040
54452
  buffer = "";
54041
54453
  onScroll = null;
54042
54454
  onActivity = null;
54455
+ onPointer = null;
54043
54456
  flushTimer = null;
54044
- constructor(scrollHandler, activityHandler) {
54457
+ constructor(scrollHandler, activityHandler, pointerHandler) {
54045
54458
  super();
54046
54459
  this.onScroll = scrollHandler;
54047
54460
  this.onActivity = activityHandler ?? null;
54461
+ this.onPointer = pointerHandler ?? null;
54048
54462
  }
54049
54463
  _transform(chunk, _encoding, callback) {
54050
54464
  this.buffer += chunk.toString();
@@ -54060,10 +54474,20 @@ var init_mouse_filter = __esm({
54060
54474
  if (mouseMatch) {
54061
54475
  const btn = parseInt(mouseMatch[1]);
54062
54476
  const row = parseInt(mouseMatch[3]);
54477
+ const col = parseInt(mouseMatch[2]);
54478
+ const suffix = mouseMatch[4];
54063
54479
  if ((btn === 64 || btn === 96) && this.onScroll)
54064
54480
  this.onScroll("up", 3, row);
54065
54481
  else if ((btn === 65 || btn === 97) && this.onScroll)
54066
54482
  this.onScroll("down", 3, row);
54483
+ else if (this.onPointer) {
54484
+ if (btn === 0 && suffix === "M")
54485
+ this.onPointer("press", col, row);
54486
+ else if (btn === 32 && suffix === "M")
54487
+ this.onPointer("drag", col, row);
54488
+ else if (btn === 0 && suffix === "m")
54489
+ this.onPointer("release", col, row);
54490
+ }
54067
54491
  if (this.onActivity)
54068
54492
  this.onActivity();
54069
54493
  i += mouseMatch[0].length;
@@ -54122,7 +54546,7 @@ import { createRequire as createRequire2 } from "node:module";
54122
54546
  import { fileURLToPath as fileURLToPath12 } from "node:url";
54123
54547
  import { readFileSync as readFileSync32, writeFileSync as writeFileSync18, appendFileSync as appendFileSync4, rmSync as rmSync2, readdirSync as readdirSync16, mkdirSync as mkdirSync19 } from "node:fs";
54124
54548
  import { existsSync as existsSync43 } from "node:fs";
54125
- import { execSync as execSync29 } from "node:child_process";
54549
+ import { execSync as execSync30 } from "node:child_process";
54126
54550
  import { homedir as homedir13 } from "node:os";
54127
54551
  function formatTimeAgo(date) {
54128
54552
  const seconds = Math.floor((Date.now() - date.getTime()) / 1e3);
@@ -55518,7 +55942,10 @@ async function startInteractive(config, repoPath) {
55518
55942
  if (process.stdout.isTTY) {
55519
55943
  const scrollTop = carouselLines > 0 ? carouselLines : 1;
55520
55944
  statusBar.activate(scrollTop);
55521
- statusBar.setBannerRefresh(() => banner.renderCurrentFrame());
55945
+ statusBar.setBannerRefresh(() => {
55946
+ banner.renderCurrentFrame();
55947
+ statusBar.renderHeaderButtons();
55948
+ });
55522
55949
  const { onOverlayLeave: onOverlayLeave2 } = await Promise.resolve().then(() => (init_overlay_lock(), overlay_lock_exports));
55523
55950
  onOverlayLeave2(() => {
55524
55951
  banner.renderCurrentFrame();
@@ -55528,6 +55955,7 @@ async function startInteractive(config, repoPath) {
55528
55955
  if (cohereEnabled) {
55529
55956
  statusBar.setCohereActive(true);
55530
55957
  }
55958
+ statusBar.renderHeaderButtons();
55531
55959
  if (isResumed) {
55532
55960
  statusBar.beginContentWrite();
55533
55961
  const resumeMsg = hasTaskToResume ? `Updated to v${version} \u2014 picking up where you left off.` : `Updated to v${version}.`;
@@ -55907,6 +56335,8 @@ Rationale: ${proposal.rationale}${provenanceNote}`;
55907
56335
  statusBar.cancelMouseIdle();
55908
56336
  statusBar.enableMouseTracking();
55909
56337
  statusBar.scheduleMouseIdle();
56338
+ }, (type, col, row) => {
56339
+ statusBar.handlePointerEvent(type, col, row);
55910
56340
  });
55911
56341
  process.stdin.pipe(mouseFilter);
55912
56342
  const rl = readline2.createInterface({
@@ -55929,6 +56359,21 @@ Rationale: ${proposal.rationale}${provenanceNote}`;
55929
56359
  statusBar.handleResize();
55930
56360
  }
55931
56361
  });
56362
+ let commandCtxRef = null;
56363
+ statusBar.setHeaderButtonHandler((command) => {
56364
+ if (!commandCtxRef)
56365
+ return;
56366
+ (async () => {
56367
+ try {
56368
+ if (statusBar.isActive)
56369
+ statusBar.beginContentWrite();
56370
+ await handleSlashCommand(command, commandCtxRef);
56371
+ if (statusBar.isActive)
56372
+ statusBar.endContentWrite();
56373
+ } catch {
56374
+ }
56375
+ })();
56376
+ });
55932
56377
  rl.output = null;
55933
56378
  if (process.stdin.isTTY && typeof process.stdin.setRawMode === "function") {
55934
56379
  process.stdin.setRawMode(true);
@@ -57318,7 +57763,7 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
57318
57763
  try {
57319
57764
  if (process.platform === "win32") {
57320
57765
  try {
57321
- execSync29(`taskkill /F /PID ${pid}`, { timeout: 5e3, stdio: "ignore" });
57766
+ execSync30(`taskkill /F /PID ${pid}`, { timeout: 5e3, stdio: "ignore" });
57322
57767
  } catch {
57323
57768
  }
57324
57769
  } else {
@@ -57345,7 +57790,7 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
57345
57790
  if (pid > 0) {
57346
57791
  if (process.platform === "win32") {
57347
57792
  try {
57348
- execSync29(`taskkill /F /PID ${pid}`, { timeout: 5e3, stdio: "ignore" });
57793
+ execSync30(`taskkill /F /PID ${pid}`, { timeout: 5e3, stdio: "ignore" });
57349
57794
  } catch {
57350
57795
  }
57351
57796
  } else {
@@ -57362,7 +57807,7 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
57362
57807
  } catch {
57363
57808
  }
57364
57809
  try {
57365
- execSync29(process.platform === "win32" ? "timeout /t 1 /nobreak >nul" : "sleep 0.5", { timeout: 3e3, stdio: "ignore" });
57810
+ execSync30(process.platform === "win32" ? "timeout /t 1 /nobreak >nul" : "sleep 0.5", { timeout: 3e3, stdio: "ignore" });
57366
57811
  } catch {
57367
57812
  }
57368
57813
  const oaPath = join59(repoRoot, OA_DIR);
@@ -57376,14 +57821,14 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
57376
57821
  } catch (err) {
57377
57822
  if (attempt < 2) {
57378
57823
  try {
57379
- execSync29(process.platform === "win32" ? "timeout /t 1 /nobreak >nul" : "sleep 0.3", { timeout: 3e3, stdio: "ignore" });
57824
+ execSync30(process.platform === "win32" ? "timeout /t 1 /nobreak >nul" : "sleep 0.3", { timeout: 3e3, stdio: "ignore" });
57380
57825
  } catch {
57381
57826
  }
57382
57827
  } else {
57383
57828
  writeContent(() => renderWarning(`Could not fully remove ${OA_DIR}/: ${err instanceof Error ? err.message : String(err)}`));
57384
57829
  if (process.platform === "win32") {
57385
57830
  try {
57386
- execSync29(`rd /s /q "${oaPath}"`, { timeout: 1e4, stdio: "ignore" });
57831
+ execSync30(`rd /s /q "${oaPath}"`, { timeout: 1e4, stdio: "ignore" });
57387
57832
  deleted = true;
57388
57833
  } catch {
57389
57834
  }
@@ -57432,6 +57877,7 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
57432
57877
  return { entries: ctx.entries.length, lastSaved: ctx.updatedAt };
57433
57878
  }
57434
57879
  };
57880
+ commandCtxRef = commandCtx;
57435
57881
  showPrompt();
57436
57882
  if (!isResumed) {
57437
57883
  const savedCtx = loadSessionContext(repoRoot);
@@ -58323,19 +58769,48 @@ async function runWithTUI(task, config, repoPath) {
58323
58769
  try {
58324
58770
  const ts = handle.runner?.taskState;
58325
58771
  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) {
58772
+ const extractionBackend = handle.runner.getBackend();
58773
+ const modFiles = Array.from(ts.modifiedFiles.entries()).map(([p, a]) => `${p} (${a})`).join(", ");
58774
+ const trajectoryPrompt = `You are a memory extraction system. Given this completed task trajectory, extract ONE concise reusable lesson.
58775
+
58776
+ TASK: ${task.slice(0, 300)}
58777
+ COMPLETED STEPS: ${(ts.completedSteps || []).slice(-5).join("; ")}
58778
+ FAILED APPROACHES: ${(ts.failedApproaches || []).join("; ") || "none"}
58779
+ FILES MODIFIED: ${modFiles || "none"}
58780
+ TOOL CALLS: ${ts.toolCallCount}
58781
+
58782
+ Extract the lesson in EXACTLY this format (no other text):
58783
+ CATEGORY: strategy|recovery|optimization
58784
+ TRIGGER: <what problem signature activates this \u2014 one line>
58785
+ LESSON: <one-sentence generalizable rule \u2014 the actual fix pattern, NOT error messages>
58786
+ STEPS: <ordered concrete steps, semicolon-separated>
58787
+
58788
+ Rules:
58789
+ - LESSON must describe the FIX PATTERN (e.g. "use ?? null after Map.get()"), never the error text
58790
+ - Keep total response under 200 characters for LESSON field
58791
+ - Use descriptive variable names for non-fixed elements (e.g. "the target file" not "/src/foo.ts")
58792
+ - If FAILED APPROACHES exist, category MUST be "recovery"`;
58793
+ const extractionResp = await extractionBackend.chatCompletion({
58794
+ messages: [
58795
+ { role: "system", content: "You extract structured procedural memories from task trajectories. Respond ONLY in the requested format." },
58796
+ { role: "user", content: trajectoryPrompt }
58797
+ ],
58798
+ tools: [],
58799
+ temperature: 0,
58800
+ maxTokens: 300,
58801
+ timeoutMs: 3e4
58802
+ });
58803
+ const raw = extractionResp.choices?.[0]?.message?.content || "";
58804
+ const categoryMatch = raw.match(/CATEGORY:\s*(strategy|recovery|optimization)/i);
58805
+ const triggerMatch = raw.match(/TRIGGER:\s*(.+)/i);
58806
+ const lessonMatch = raw.match(/LESSON:\s*(.+)/i);
58807
+ const stepsMatch = raw.match(/STEPS:\s*(.+)/i);
58808
+ const category = categoryMatch?.[1]?.toLowerCase() || "strategy";
58809
+ const trigger = triggerMatch?.[1]?.trim().slice(0, 150) || "";
58810
+ const lesson = lessonMatch?.[1]?.trim().slice(0, 200) || "";
58811
+ const steps = stepsMatch?.[1]?.trim().slice(0, 300) || "";
58812
+ if (lesson && lesson.length > 5) {
58813
+ const content = `[${category}] TRIGGER: ${trigger} | LESSON: ${lesson}` + (steps ? ` | STEPS: ${steps}` : "");
58339
58814
  const metaDir = join59(repoRoot, ".oa", "memory", "metabolism");
58340
58815
  const storeFile = join59(metaDir, "store.json");
58341
58816
  let store = [];
@@ -58347,15 +58822,15 @@ async function runWithTUI(task, config, repoPath) {
58347
58822
  store.push({
58348
58823
  id: `mem-traj-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 6)}`,
58349
58824
  type: "procedural",
58350
- content: lesson.slice(0, 500),
58351
- sourceTrace: "trajectory-extraction",
58825
+ content: content.slice(0, 600),
58826
+ sourceTrace: "llm-trajectory-extraction",
58352
58827
  scores: {
58353
- novelty: 0.6,
58354
- utility: category === "recovery" ? 0.8 : 0.6,
58355
- confidence: 0.7,
58828
+ novelty: 0.7,
58829
+ utility: category === "recovery" ? 0.85 : 0.7,
58830
+ confidence: 0.8,
58356
58831
  identityRelevance: 0.3
58357
58832
  },
58358
- decision: { action: "admit", reason: `Auto-extracted ${category} from task (${ts.toolCallCount} tool calls)` },
58833
+ decision: { action: "admit", reason: `LLM-extracted ${category} from task (${ts.toolCallCount} tool calls)` },
58359
58834
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
58360
58835
  lastAccessedAt: (/* @__PURE__ */ new Date()).toISOString(),
58361
58836
  accessCount: 0
@@ -58382,7 +58857,7 @@ async function runWithTUI(task, config, repoPath) {
58382
58857
  const metaFile = join59(repoRoot, ".oa", "memory", "metabolism", "store.json");
58383
58858
  if (existsSync43(metaFile)) {
58384
58859
  const store = JSON.parse(readFileSync32(metaFile, "utf8"));
58385
- const latest = store.filter((m) => m.sourceTrace === "trajectory-extraction").slice(-1)[0];
58860
+ const latest = store.filter((m) => m.sourceTrace === "trajectory-extraction" || m.sourceTrace === "llm-trajectory-extraction").slice(-1)[0];
58386
58861
  if (latest && latest.scores?.confidence >= 0.6) {
58387
58862
  try {
58388
58863
  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.83",
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",