open-agents-ai 0.138.80 → 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 +620 -42
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -5667,6 +5667,21 @@ async function handleCmd(cmd) {
5667
5667
  }
5668
5668
  }
5669
5669
  } catch {}
5670
+ // WO-DL4: IPFS pin the insight content for persistence + cross-node retrieval
5671
+ var _cpiCid = null;
5672
+ try {
5673
+ var _cpiHeliaReady = await _ensureHelia();
5674
+ if (_cpiHeliaReady && _heliaFs) {
5675
+ var _cpiBytes = new TextEncoder().encode(JSON.stringify({ insight: _cpiInsight, category: _cpiCategory, confidence: _cpiConfidence, tier: _cpiTier, agent: agentName, ts: Date.now() }));
5676
+ var _cpiCidObj = await _heliaFs.addBytes(_cpiBytes);
5677
+ for await (var _cpiPin of _heliaNode.pins.add(_cpiCidObj)) {}
5678
+ _cpiCid = _cpiCidObj.toString();
5679
+ dlog('IPFS pinned insight: ' + _cpiCid);
5680
+ }
5681
+ } catch (_cpiIpfsErr) {
5682
+ dlog('IPFS pin failed (graceful): ' + (_cpiIpfsErr.message || _cpiIpfsErr));
5683
+ }
5684
+
5670
5685
  var _cpiDelta = {
5671
5686
  type: 'cohere.learning',
5672
5687
  delta_id: 'insight-' + Date.now().toString(36) + '-' + Math.random().toString(36).slice(2, 6),
@@ -5676,6 +5691,7 @@ async function handleCmd(cmd) {
5676
5691
  category: _cpiCategory,
5677
5692
  confidence: _cpiConfidence,
5678
5693
  model_tier: _cpiTier,
5694
+ cid: _cpiCid, // null if IPFS unavailable, CID string if pinned
5679
5695
  timestamp: Date.now(),
5680
5696
  };
5681
5697
  _natsConn.publish('nexus.cohere.learning', _natsCodec.encode(JSON.stringify(_cpiDelta)));
@@ -6971,6 +6987,31 @@ process.on('unhandledRejection', (reason) => {
6971
6987
  mkdirSync(_lStoreDir, { recursive: true });
6972
6988
  writeFileSync(_lStoreFile, JSON.stringify(_lStore, null, 2));
6973
6989
  dlog('COHERE learning ingested from ' + (_lData.source_agent || 'unknown') + ': ' + String(_lData.insight).slice(0, 60));
6990
+
6991
+ // WO-DL4: Cross-pin the CID locally if provided (content persistence)
6992
+ if (_lData.cid && _lData.cid.startsWith('bafy')) {
6993
+ try {
6994
+ var _lCidDir = join(nexusDir, 'ipfs', 'cid-registry');
6995
+ mkdirSync(_lCidDir, { recursive: true });
6996
+ var _lCidFile = join(_lCidDir, 'learning-cids.json');
6997
+ var _lCids = {};
6998
+ try { if (existsSync(_lCidFile)) _lCids = JSON.parse(readFileSync(_lCidFile, 'utf8')); } catch {}
6999
+ _lCids[_lData.delta_id] = { cid: _lData.cid, source: _lData.source_agent, pinned: false, timestamp: Date.now() };
7000
+ writeFileSync(_lCidFile, JSON.stringify(_lCids, null, 2));
7001
+ dlog('CID registered: ' + _lData.cid.slice(0, 20) + '... from ' + _lData.source_agent);
7002
+ // Attempt cross-pin if Helia is ready
7003
+ try {
7004
+ if (_heliaReady && _heliaNode) {
7005
+ var { CID: CIDClass } = await import('multiformats/cid');
7006
+ var _lCidObj = CIDClass.parse(_lData.cid);
7007
+ for await (var _lPin of _heliaNode.pins.add(_lCidObj)) {}
7008
+ _lCids[_lData.delta_id].pinned = true;
7009
+ writeFileSync(_lCidFile, JSON.stringify(_lCids, null, 2));
7010
+ dlog('Cross-pinned CID: ' + _lData.cid.slice(0, 20));
7011
+ }
7012
+ } catch {}
7013
+ } catch {}
7014
+ }
6974
7015
  } catch {}
6975
7016
  }
6976
7017
  })().catch(function(e) { dlog('COHERE learning listener error: ' + (e.message || e)); });
@@ -7032,6 +7073,69 @@ process.on('unhandledRejection', (reason) => {
7032
7073
  if (cohereActive) _publishCapacityAnnouncement();
7033
7074
  }, 60000);
7034
7075
 
7076
+ // \u2500\u2500 WO-DL3: Epoch sync \u2014 hash-based state comparison every 5 min \u2500\u2500
7077
+ // Each node publishes a lightweight fingerprint of its memory state.
7078
+ // If hashes differ between same-tier nodes, the lagging node can
7079
+ // request missing insights via nexus.cohere.learning.request.
7080
+ var _epochCounter = 0;
7081
+ function _publishEpochSync() {
7082
+ if (!cohereActive || !_natsConn || !_natsCodec) return;
7083
+ try {
7084
+ // Read local memory store for fingerprinting
7085
+ var _esStoreFile = join(nexusDir, '..', 'memory', 'metabolism', 'store.json');
7086
+ var _esStore = [];
7087
+ try { if (existsSync(_esStoreFile)) _esStore = JSON.parse(readFileSync(_esStoreFile, 'utf8')); } catch {}
7088
+
7089
+ // Compute top-10 memory IDs sorted by utility*confidence (deterministic)
7090
+ var _esFiltered = _esStore
7091
+ .filter(function(m) { return m.type !== 'quarantine' && m.scores && m.scores.confidence > 0.15; })
7092
+ .sort(function(a, b) { return (b.scores.utility * b.scores.confidence) - (a.scores.utility * a.scores.confidence); })
7093
+ .slice(0, 10);
7094
+ var _esTopIds = _esFiltered.map(function(m) { return m.id; }).sort().join(',');
7095
+
7096
+ // SHA-256 hash of top-10 IDs for compact comparison
7097
+ var _esHash = require('crypto').createHash('sha256').update(_esTopIds).digest('hex').slice(0, 16);
7098
+
7099
+ _epochCounter++;
7100
+ var _esAnnouncement = {
7101
+ type: 'cohere.epoch',
7102
+ peer: nexus.peerId,
7103
+ agentName: agentName,
7104
+ epoch: _epochCounter,
7105
+ memoryCount: _esStore.length,
7106
+ topHash: _esHash,
7107
+ insightsAvailable: _esFiltered.length,
7108
+ timestamp: Date.now(),
7109
+ };
7110
+ _natsConn.publish('nexus.cohere.learning.epoch', _natsCodec.encode(JSON.stringify(_esAnnouncement)));
7111
+ dlog('Epoch sync published: epoch=' + _epochCounter + ' memories=' + _esStore.length + ' hash=' + _esHash);
7112
+ } catch (e) {
7113
+ dlog('Epoch sync error: ' + (e.message || e));
7114
+ }
7115
+ }
7116
+
7117
+ // Epoch sync every 5 minutes
7118
+ setInterval(function() {
7119
+ if (cohereActive) _publishEpochSync();
7120
+ }, 300000);
7121
+ // Also publish on startup after a brief delay
7122
+ setTimeout(function() { if (cohereActive) _publishEpochSync(); }, 10000);
7123
+
7124
+ // Listen for epoch announcements from other nodes
7125
+ var _epochSub = _natsConn.subscribe('nexus.cohere.learning.epoch');
7126
+ (async function() {
7127
+ for await (var _eMsg of _epochSub) {
7128
+ try {
7129
+ var _eData = JSON.parse(_natsCodec.decode(_eMsg.data));
7130
+ if (!_eData.peer || _eData.peer === nexus.peerId) continue;
7131
+ // Log peer epoch state for dashboard visibility
7132
+ dlog('Epoch from ' + (_eData.agentName || 'peer') + ': epoch=' + _eData.epoch + ' memories=' + _eData.memoryCount + ' hash=' + _eData.topHash);
7133
+ // Future: if our hash differs and they have more insights, request sync
7134
+ } catch {}
7135
+ }
7136
+ })().catch(function(e) { dlog('Epoch listener error: ' + (e.message || e)); });
7137
+ dlog('Epoch sync listener active on nexus.cohere.learning.epoch');
7138
+
7035
7139
  // Lazy-init IPFS in background (non-blocking)
7036
7140
  _ensureHelia().catch(function() {});
7037
7141
  }
@@ -22200,6 +22304,10 @@ var init_agenticRunner = __esm({
22200
22304
  get taskState() {
22201
22305
  return this._taskState;
22202
22306
  }
22307
+ /** Get the backend (for post-task LLM extraction calls) */
22308
+ getBackend() {
22309
+ return this.backend;
22310
+ }
22203
22311
  /** Get the file state registry (for external inspection) */
22204
22312
  get fileRegistry() {
22205
22313
  return this._fileRegistry;
@@ -43781,7 +43889,7 @@ async function handlePeerEndpoint(peerId, authKey, ctx, local) {
43781
43889
  }
43782
43890
  }
43783
43891
  async function handleParallel(arg, ctx) {
43784
- const { execSync: execSync30 } = await import("node:child_process");
43892
+ const { execSync: execSync31 } = await import("node:child_process");
43785
43893
  const baseUrl = ctx.config.backendUrl || "http://localhost:11434";
43786
43894
  const isRemote = ctx.config.backendType === "nexus";
43787
43895
  if (isRemote) {
@@ -43805,7 +43913,7 @@ async function handleParallel(arg, ctx) {
43805
43913
  }
43806
43914
  let systemdVal = "";
43807
43915
  try {
43808
- 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" });
43809
43917
  const match = out.match(/OLLAMA_NUM_PARALLEL=(\d+)/);
43810
43918
  if (match)
43811
43919
  systemdVal = match[1];
@@ -43834,7 +43942,7 @@ async function handleParallel(arg, ctx) {
43834
43942
  }
43835
43943
  const isSystemd = (() => {
43836
43944
  try {
43837
- 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();
43838
43946
  return out === "active" || out === "inactive";
43839
43947
  } catch {
43840
43948
  return false;
@@ -43848,10 +43956,10 @@ async function handleParallel(arg, ctx) {
43848
43956
  const overrideContent = `[Service]
43849
43957
  Environment="OLLAMA_NUM_PARALLEL=${n}"
43850
43958
  `;
43851
- execSync30(`sudo mkdir -p ${overrideDir}`, { stdio: "pipe" });
43852
- execSync30(`echo '${overrideContent}' | sudo tee ${overrideFile} > /dev/null`, { stdio: "pipe" });
43853
- execSync30("sudo systemctl daemon-reload", { stdio: "pipe" });
43854
- 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" });
43855
43963
  let ready = false;
43856
43964
  for (let i = 0; i < 30 && !ready; i++) {
43857
43965
  await new Promise((r) => setTimeout(r, 500));
@@ -43878,7 +43986,7 @@ Environment="OLLAMA_NUM_PARALLEL=${n}"
43878
43986
  renderInfo(`Setting OLLAMA_NUM_PARALLEL=${n}...`);
43879
43987
  try {
43880
43988
  try {
43881
- execSync30("pkill -f 'ollama serve' 2>/dev/null || true", { stdio: "pipe" });
43989
+ execSync31("pkill -f 'ollama serve' 2>/dev/null || true", { stdio: "pipe" });
43882
43990
  } catch {
43883
43991
  }
43884
43992
  await new Promise((r) => setTimeout(r, 1e3));
@@ -44630,18 +44738,18 @@ async function showExposeDashboard(gateway, rl, ctx) {
44630
44738
  const cmd = `/endpoint ${id} --auth ${gateway.authKey ?? ""}`;
44631
44739
  let copied = false;
44632
44740
  try {
44633
- const { execSync: execSync30 } = __require("node:child_process");
44741
+ const { execSync: execSync31 } = __require("node:child_process");
44634
44742
  const platform5 = process.platform;
44635
44743
  if (platform5 === "darwin") {
44636
- execSync30("pbcopy", { input: cmd, timeout: 3e3 });
44744
+ execSync31("pbcopy", { input: cmd, timeout: 3e3 });
44637
44745
  copied = true;
44638
44746
  } else if (platform5 === "win32") {
44639
- execSync30("clip", { input: cmd, timeout: 3e3 });
44747
+ execSync31("clip", { input: cmd, timeout: 3e3 });
44640
44748
  copied = true;
44641
44749
  } else {
44642
44750
  for (const tool of ["xclip -selection clipboard", "xsel --clipboard --input", "wl-copy"]) {
44643
44751
  try {
44644
- execSync30(tool, { input: cmd, timeout: 3e3, stdio: ["pipe", "pipe", "pipe"] });
44752
+ execSync31(tool, { input: cmd, timeout: 3e3, stdio: ["pipe", "pipe", "pipe"] });
44645
44753
  copied = true;
44646
44754
  break;
44647
44755
  } catch {
@@ -52079,15 +52187,301 @@ var init_system_metrics = __esm({
52079
52187
  }
52080
52188
  });
52081
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
+
52082
52475
  // packages/cli/dist/tui/status-bar.js
52083
52476
  import { readFileSync as readFileSync31 } from "node:fs";
52084
- 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;
52085
52478
  var init_status_bar = __esm({
52086
52479
  "packages/cli/dist/tui/status-bar.js"() {
52087
52480
  "use strict";
52088
52481
  init_render();
52089
52482
  init_braille_spinner();
52090
52483
  init_system_metrics();
52484
+ init_text_selection();
52091
52485
  EXPERT_TOOL_BASELINES = {
52092
52486
  file_read: 12,
52093
52487
  structured_read: 15,
@@ -52247,6 +52641,7 @@ var init_status_bar = __esm({
52247
52641
  };
52248
52642
  PANEL_BG = 234;
52249
52643
  CONTENT_BG = 233;
52644
+ TEXT_PRIMARY = 178;
52250
52645
  PANEL_BG_SEQ = `\x1B[48;5;${PANEL_BG}m`;
52251
52646
  CONTENT_BG_SEQ = `\x1B[48;5;${CONTENT_BG}m`;
52252
52647
  RESET = "\x1B[0m";
@@ -52274,6 +52669,15 @@ var init_status_bar = __esm({
52274
52669
  _mouseIdleTimer = null;
52275
52670
  static MOUSE_IDLE_MS = 1500;
52276
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;
52277
52681
  /** Banner refresh callback — called after resize to re-render top rows */
52278
52682
  _bannerRefresh = null;
52279
52683
  /** Track previous terminal dimensions to clear ghost separators on resize */
@@ -52917,6 +53321,91 @@ var init_status_bar = __esm({
52917
53321
  this._mouseIdleTimer = null;
52918
53322
  }
52919
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
+ }
52920
53409
  /** Set a callback to re-render the banner after resize/redraw */
52921
53410
  setBannerRefresh(refresh) {
52922
53411
  this._bannerRefresh = refresh;
@@ -53111,6 +53600,7 @@ ${CONTENT_BG_SEQ}`);
53111
53600
  scrollContentUp(lines = 1) {
53112
53601
  if (!this.active)
53113
53602
  return;
53603
+ this._textSelection.clear();
53114
53604
  this.cancelMouseIdle();
53115
53605
  this.enableMouseTracking();
53116
53606
  const maxOffset = Math.max(0, this._contentLines.length - this.contentHeight);
@@ -53122,6 +53612,7 @@ ${CONTENT_BG_SEQ}`);
53122
53612
  scrollContentDown(lines = 1) {
53123
53613
  if (!this.active)
53124
53614
  return;
53615
+ this._textSelection.clear();
53125
53616
  this.cancelMouseIdle();
53126
53617
  this.enableMouseTracking();
53127
53618
  this._contentScrollOffset = Math.max(0, this._contentScrollOffset - lines);
@@ -53158,10 +53649,18 @@ ${CONTENT_BG_SEQ}`);
53158
53649
  let buf = "\x1B[?2026h";
53159
53650
  buf += "\x1B7";
53160
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);
53161
53656
  for (let row = 0; row < h; row++) {
53162
53657
  const lineIdx = startIdx + row;
53163
- const line = lineIdx < totalLines ? this._contentLines[lineIdx] : "";
53658
+ let line = lineIdx < totalLines ? this._contentLines[lineIdx] : "";
53164
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
+ }
53165
53664
  buf += `\x1B[${screenRow};1H${CONTENT_BG_SEQ}\x1B[2K${line}`;
53166
53665
  }
53167
53666
  if (this._contentScrollOffset > 0) {
@@ -53813,6 +54312,22 @@ ${CONTENT_BG_SEQ}`);
53813
54312
  onCtrlO();
53814
54313
  return;
53815
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
+ }
53816
54331
  if (s && (/\x1B\[</.test(s) || /^\d+;\d+;\d*[Mm]/.test(s) || /^;\d+[Mm]/.test(s) || /\[<\d/.test(s))) {
53817
54332
  return;
53818
54333
  }
@@ -53936,11 +54451,13 @@ var init_mouse_filter = __esm({
53936
54451
  buffer = "";
53937
54452
  onScroll = null;
53938
54453
  onActivity = null;
54454
+ onPointer = null;
53939
54455
  flushTimer = null;
53940
- constructor(scrollHandler, activityHandler) {
54456
+ constructor(scrollHandler, activityHandler, pointerHandler) {
53941
54457
  super();
53942
54458
  this.onScroll = scrollHandler;
53943
54459
  this.onActivity = activityHandler ?? null;
54460
+ this.onPointer = pointerHandler ?? null;
53944
54461
  }
53945
54462
  _transform(chunk, _encoding, callback) {
53946
54463
  this.buffer += chunk.toString();
@@ -53956,10 +54473,20 @@ var init_mouse_filter = __esm({
53956
54473
  if (mouseMatch) {
53957
54474
  const btn = parseInt(mouseMatch[1]);
53958
54475
  const row = parseInt(mouseMatch[3]);
54476
+ const col = parseInt(mouseMatch[2]);
54477
+ const suffix = mouseMatch[4];
53959
54478
  if ((btn === 64 || btn === 96) && this.onScroll)
53960
54479
  this.onScroll("up", 3, row);
53961
54480
  else if ((btn === 65 || btn === 97) && this.onScroll)
53962
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
+ }
53963
54490
  if (this.onActivity)
53964
54491
  this.onActivity();
53965
54492
  i += mouseMatch[0].length;
@@ -54018,7 +54545,7 @@ import { createRequire as createRequire2 } from "node:module";
54018
54545
  import { fileURLToPath as fileURLToPath12 } from "node:url";
54019
54546
  import { readFileSync as readFileSync32, writeFileSync as writeFileSync18, appendFileSync as appendFileSync4, rmSync as rmSync2, readdirSync as readdirSync16, mkdirSync as mkdirSync19 } from "node:fs";
54020
54547
  import { existsSync as existsSync43 } from "node:fs";
54021
- import { execSync as execSync29 } from "node:child_process";
54548
+ import { execSync as execSync30 } from "node:child_process";
54022
54549
  import { homedir as homedir13 } from "node:os";
54023
54550
  function formatTimeAgo(date) {
54024
54551
  const seconds = Math.floor((Date.now() - date.getTime()) / 1e3);
@@ -55414,7 +55941,10 @@ async function startInteractive(config, repoPath) {
55414
55941
  if (process.stdout.isTTY) {
55415
55942
  const scrollTop = carouselLines > 0 ? carouselLines : 1;
55416
55943
  statusBar.activate(scrollTop);
55417
- statusBar.setBannerRefresh(() => banner.renderCurrentFrame());
55944
+ statusBar.setBannerRefresh(() => {
55945
+ banner.renderCurrentFrame();
55946
+ statusBar.renderHeaderButtons();
55947
+ });
55418
55948
  const { onOverlayLeave: onOverlayLeave2 } = await Promise.resolve().then(() => (init_overlay_lock(), overlay_lock_exports));
55419
55949
  onOverlayLeave2(() => {
55420
55950
  banner.renderCurrentFrame();
@@ -55424,6 +55954,7 @@ async function startInteractive(config, repoPath) {
55424
55954
  if (cohereEnabled) {
55425
55955
  statusBar.setCohereActive(true);
55426
55956
  }
55957
+ statusBar.renderHeaderButtons();
55427
55958
  if (isResumed) {
55428
55959
  statusBar.beginContentWrite();
55429
55960
  const resumeMsg = hasTaskToResume ? `Updated to v${version} \u2014 picking up where you left off.` : `Updated to v${version}.`;
@@ -55803,6 +56334,8 @@ Rationale: ${proposal.rationale}${provenanceNote}`;
55803
56334
  statusBar.cancelMouseIdle();
55804
56335
  statusBar.enableMouseTracking();
55805
56336
  statusBar.scheduleMouseIdle();
56337
+ }, (type, col, row) => {
56338
+ statusBar.handlePointerEvent(type, col, row);
55806
56339
  });
55807
56340
  process.stdin.pipe(mouseFilter);
55808
56341
  const rl = readline2.createInterface({
@@ -55825,6 +56358,21 @@ Rationale: ${proposal.rationale}${provenanceNote}`;
55825
56358
  statusBar.handleResize();
55826
56359
  }
55827
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
+ });
55828
56376
  rl.output = null;
55829
56377
  if (process.stdin.isTTY && typeof process.stdin.setRawMode === "function") {
55830
56378
  process.stdin.setRawMode(true);
@@ -57214,7 +57762,7 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
57214
57762
  try {
57215
57763
  if (process.platform === "win32") {
57216
57764
  try {
57217
- execSync29(`taskkill /F /PID ${pid}`, { timeout: 5e3, stdio: "ignore" });
57765
+ execSync30(`taskkill /F /PID ${pid}`, { timeout: 5e3, stdio: "ignore" });
57218
57766
  } catch {
57219
57767
  }
57220
57768
  } else {
@@ -57241,7 +57789,7 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
57241
57789
  if (pid > 0) {
57242
57790
  if (process.platform === "win32") {
57243
57791
  try {
57244
- execSync29(`taskkill /F /PID ${pid}`, { timeout: 5e3, stdio: "ignore" });
57792
+ execSync30(`taskkill /F /PID ${pid}`, { timeout: 5e3, stdio: "ignore" });
57245
57793
  } catch {
57246
57794
  }
57247
57795
  } else {
@@ -57258,7 +57806,7 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
57258
57806
  } catch {
57259
57807
  }
57260
57808
  try {
57261
- 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" });
57262
57810
  } catch {
57263
57811
  }
57264
57812
  const oaPath = join59(repoRoot, OA_DIR);
@@ -57272,14 +57820,14 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
57272
57820
  } catch (err) {
57273
57821
  if (attempt < 2) {
57274
57822
  try {
57275
- 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" });
57276
57824
  } catch {
57277
57825
  }
57278
57826
  } else {
57279
57827
  writeContent(() => renderWarning(`Could not fully remove ${OA_DIR}/: ${err instanceof Error ? err.message : String(err)}`));
57280
57828
  if (process.platform === "win32") {
57281
57829
  try {
57282
- execSync29(`rd /s /q "${oaPath}"`, { timeout: 1e4, stdio: "ignore" });
57830
+ execSync30(`rd /s /q "${oaPath}"`, { timeout: 1e4, stdio: "ignore" });
57283
57831
  deleted = true;
57284
57832
  } catch {
57285
57833
  }
@@ -57328,6 +57876,7 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
57328
57876
  return { entries: ctx.entries.length, lastSaved: ctx.updatedAt };
57329
57877
  }
57330
57878
  };
57879
+ commandCtxRef = commandCtx;
57331
57880
  showPrompt();
57332
57881
  if (!isResumed) {
57333
57882
  const savedCtx = loadSessionContext(repoRoot);
@@ -58219,19 +58768,48 @@ async function runWithTUI(task, config, repoPath) {
58219
58768
  try {
58220
58769
  const ts = handle.runner?.taskState;
58221
58770
  if (ts && ts.toolCallCount > 2) {
58222
- let category = "strategy";
58223
- let lesson = "";
58224
- if (ts.failedApproaches && ts.failedApproaches.length > 0) {
58225
- category = "recovery";
58226
- const failed = ts.failedApproaches.slice(0, 2).join("; ");
58227
- const worked = ts.completedSteps?.slice(-2).join(" \u2192 ") || "completed task";
58228
- lesson = `[recovery] Avoid: ${failed}. Instead: ${worked}`;
58229
- } else if (ts.completedSteps && ts.completedSteps.length > 1) {
58230
- lesson = `[strategy] ${ts.completedSteps.slice(-3).join(" \u2192 ")}`;
58231
- } else {
58232
- lesson = `[strategy] Solved: ${task.slice(0, 150)}`;
58233
- }
58234
- 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}` : "");
58235
58813
  const metaDir = join59(repoRoot, ".oa", "memory", "metabolism");
58236
58814
  const storeFile = join59(metaDir, "store.json");
58237
58815
  let store = [];
@@ -58243,15 +58821,15 @@ async function runWithTUI(task, config, repoPath) {
58243
58821
  store.push({
58244
58822
  id: `mem-traj-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 6)}`,
58245
58823
  type: "procedural",
58246
- content: lesson.slice(0, 500),
58247
- sourceTrace: "trajectory-extraction",
58824
+ content: content.slice(0, 600),
58825
+ sourceTrace: "llm-trajectory-extraction",
58248
58826
  scores: {
58249
- novelty: 0.6,
58250
- utility: category === "recovery" ? 0.8 : 0.6,
58251
- confidence: 0.7,
58827
+ novelty: 0.7,
58828
+ utility: category === "recovery" ? 0.85 : 0.7,
58829
+ confidence: 0.8,
58252
58830
  identityRelevance: 0.3
58253
58831
  },
58254
- 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)` },
58255
58833
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
58256
58834
  lastAccessedAt: (/* @__PURE__ */ new Date()).toISOString(),
58257
58835
  accessCount: 0
@@ -58278,7 +58856,7 @@ async function runWithTUI(task, config, repoPath) {
58278
58856
  const metaFile = join59(repoRoot, ".oa", "memory", "metabolism", "store.json");
58279
58857
  if (existsSync43(metaFile)) {
58280
58858
  const store = JSON.parse(readFileSync32(metaFile, "utf8"));
58281
- 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];
58282
58860
  if (latest && latest.scores?.confidence >= 0.6) {
58283
58861
  try {
58284
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.80",
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",