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.
- package/dist/index.js +620 -42
- 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:
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
43852
|
-
|
|
43853
|
-
|
|
43854
|
-
|
|
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
|
-
|
|
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:
|
|
44741
|
+
const { execSync: execSync31 } = __require("node:child_process");
|
|
44634
44742
|
const platform5 = process.platform;
|
|
44635
44743
|
if (platform5 === "darwin") {
|
|
44636
|
-
|
|
44744
|
+
execSync31("pbcopy", { input: cmd, timeout: 3e3 });
|
|
44637
44745
|
copied = true;
|
|
44638
44746
|
} else if (platform5 === "win32") {
|
|
44639
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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(() =>
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
58223
|
-
|
|
58224
|
-
|
|
58225
|
-
|
|
58226
|
-
|
|
58227
|
-
|
|
58228
|
-
|
|
58229
|
-
|
|
58230
|
-
|
|
58231
|
-
|
|
58232
|
-
|
|
58233
|
-
|
|
58234
|
-
|
|
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:
|
|
58247
|
-
sourceTrace: "trajectory-extraction",
|
|
58824
|
+
content: content.slice(0, 600),
|
|
58825
|
+
sourceTrace: "llm-trajectory-extraction",
|
|
58248
58826
|
scores: {
|
|
58249
|
-
novelty: 0.
|
|
58250
|
-
utility: category === "recovery" ? 0.
|
|
58251
|
-
confidence: 0.
|
|
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: `
|
|
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