deepclause-sdk 0.0.74 → 0.0.76
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/cli/run.d.ts +3 -0
- package/dist/cli/run.d.ts.map +1 -1
- package/dist/cli/run.js +1 -0
- package/dist/cli/run.js.map +1 -1
- package/dist/cli/search.d.ts +4 -11
- package/dist/cli/search.d.ts.map +1 -1
- package/dist/cli/search.js +100 -42
- package/dist/cli/search.js.map +1 -1
- package/dist/cli/tools.js +2 -2
- package/dist/cli/tools.js.map +1 -1
- package/dist/cli/tui.d.ts +15 -0
- package/dist/cli/tui.d.ts.map +1 -1
- package/dist/cli/tui.js +719 -47
- package/dist/cli/tui.js.map +1 -1
- package/dist/runner.d.ts +1 -0
- package/dist/runner.d.ts.map +1 -1
- package/dist/runner.js +240 -188
- package/dist/runner.js.map +1 -1
- package/dist/system/runtime/conductor.d.ts +4 -0
- package/dist/system/runtime/conductor.d.ts.map +1 -1
- package/dist/system/runtime/conductor.js +2 -1
- package/dist/system/runtime/conductor.js.map +1 -1
- package/dist/system/runtime/dml-executor.d.ts +3 -0
- package/dist/system/runtime/dml-executor.d.ts.map +1 -1
- package/dist/system/runtime/dml-executor.js +4 -0
- package/dist/system/runtime/dml-executor.js.map +1 -1
- package/dist/system/runtime/runtime-tools.d.ts +3 -0
- package/dist/system/runtime/runtime-tools.d.ts.map +1 -1
- package/dist/system/runtime/runtime-tools.js +12 -5
- package/dist/system/runtime/runtime-tools.js.map +1 -1
- package/dist/tools.js +2 -2
- package/dist/tools.js.map +1 -1
- package/dist/types.d.ts +7 -1
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/vendor/swipl-wasm/README.md +253 -0
package/dist/cli/tui.js
CHANGED
|
@@ -7,10 +7,11 @@ import { listCommands } from './commands.js';
|
|
|
7
7
|
import { getConfigPath, loadConfig, resolveModelSlot, setModel } from './config.js';
|
|
8
8
|
import { run } from './run.js';
|
|
9
9
|
import { formatToolArgs } from './tool-args.js';
|
|
10
|
-
import { appendConductorSessionMessages, createSessionExecutionLogWriter, createLocalSkill, createConductorSession, getConductorSessionDetail, listConductorSessions, runConductorTurn, } from '../system/runtime/conductor.js';
|
|
10
|
+
import { appendConductorSessionMessages, createSessionExecutionLogWriter, createLocalSkill, createConductorSession, getConductorSessionDetail, listConductorSessions, mergeSessionUsage, runConductorTurn, } from '../system/runtime/conductor.js';
|
|
11
11
|
import { getSystemAssetSourcePaths } from '../system/assets/index.js';
|
|
12
12
|
import { createShellManager, describeShellExecutionBackend, } from '../system/runtime/shell-manager.js';
|
|
13
13
|
import { buildToolCompletionEvent, buildToolFailureEvent, buildToolStartEvent, createShellToolEventBridge, } from '../system/runtime/shell-tool-events.js';
|
|
14
|
+
import { recordTokenUsage } from '../system/runtime/token-usage.js';
|
|
14
15
|
const IGNORED_LIVE_LOG_TOOLS = new Set(['set_result', 'update_memory']);
|
|
15
16
|
const BUILTIN_SLASH_COMMANDS = ['new', 'sessions', 'help', 'run', 'compile', 'skill-creator', 'set-model', 'cancel', 'exit', 'quit'];
|
|
16
17
|
const MODEL_SLOTS = ['gateway', 'run', 'compile'];
|
|
@@ -63,7 +64,7 @@ export class LiveExecutionPrinter {
|
|
|
63
64
|
}
|
|
64
65
|
handle(logEvent) {
|
|
65
66
|
const { event } = logEvent;
|
|
66
|
-
if (event.type === 'usage' || event.type === 'finished') {
|
|
67
|
+
if (event.type === 'usage' || event.type === 'finished' || event.type === 'task_activity') {
|
|
67
68
|
return;
|
|
68
69
|
}
|
|
69
70
|
if (event.type === 'stream') {
|
|
@@ -147,7 +148,7 @@ export class ActivityBuffer {
|
|
|
147
148
|
activeStreamLine = '';
|
|
148
149
|
handle(logEvent) {
|
|
149
150
|
const { event } = logEvent;
|
|
150
|
-
if (event.type === 'usage' || event.type === 'finished') {
|
|
151
|
+
if (event.type === 'usage' || event.type === 'finished' || event.type === 'task_activity') {
|
|
151
152
|
return;
|
|
152
153
|
}
|
|
153
154
|
if (event.type === 'stream') {
|
|
@@ -299,12 +300,141 @@ export class ActivityBuffer {
|
|
|
299
300
|
return lines;
|
|
300
301
|
}
|
|
301
302
|
}
|
|
303
|
+
function summarizeStepDescription(raw) {
|
|
304
|
+
const firstLine = raw.split(/\r?\n/)[0] ?? '';
|
|
305
|
+
const trimmed = firstLine.replace(/\s+/g, ' ').trim();
|
|
306
|
+
return trimmed.length > 80 ? trimmed.slice(0, 77) + '\u2026' : trimmed;
|
|
307
|
+
}
|
|
308
|
+
export class TaskTracker {
|
|
309
|
+
entries = [];
|
|
310
|
+
activeStack = [];
|
|
311
|
+
maxEntries = 200;
|
|
312
|
+
handle(logEvent) {
|
|
313
|
+
const { event } = logEvent;
|
|
314
|
+
if (event.type !== 'task_activity')
|
|
315
|
+
return;
|
|
316
|
+
if (event.taskState === 'started' && event.taskId) {
|
|
317
|
+
const depth = this.activeStack.length;
|
|
318
|
+
this.activeStack.push(event.taskId);
|
|
319
|
+
this.entries.push({
|
|
320
|
+
id: event.taskId,
|
|
321
|
+
description: summarizeStepDescription(event.taskDescription ?? ''),
|
|
322
|
+
state: 'started',
|
|
323
|
+
depth,
|
|
324
|
+
startedAt: Date.now(),
|
|
325
|
+
});
|
|
326
|
+
this.trim();
|
|
327
|
+
}
|
|
328
|
+
else if ((event.taskState === 'completed' || event.taskState === 'failed') && event.taskId) {
|
|
329
|
+
const idx = this.entries.findIndex(e => e.id === event.taskId);
|
|
330
|
+
if (idx >= 0) {
|
|
331
|
+
this.entries[idx].state = event.taskState;
|
|
332
|
+
this.entries[idx].completedAt = Date.now();
|
|
333
|
+
}
|
|
334
|
+
const stackIdx = this.activeStack.lastIndexOf(event.taskId);
|
|
335
|
+
if (stackIdx >= 0) {
|
|
336
|
+
this.activeStack.splice(stackIdx, 1);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
buildPaneBody(_width) {
|
|
341
|
+
if (this.entries.length === 0) {
|
|
342
|
+
return ['No steps yet.', 'Step activity renders here.'];
|
|
343
|
+
}
|
|
344
|
+
const lines = [];
|
|
345
|
+
for (const entry of this.entries) {
|
|
346
|
+
const indent = ' '.repeat(entry.depth);
|
|
347
|
+
let marker;
|
|
348
|
+
if (entry.state === 'started') {
|
|
349
|
+
marker = '>';
|
|
350
|
+
}
|
|
351
|
+
else if (entry.state === 'completed') {
|
|
352
|
+
marker = '\u2713';
|
|
353
|
+
}
|
|
354
|
+
else {
|
|
355
|
+
marker = '\u2717';
|
|
356
|
+
}
|
|
357
|
+
lines.push(`${indent}${marker} ${entry.description}`);
|
|
358
|
+
}
|
|
359
|
+
return lines;
|
|
360
|
+
}
|
|
361
|
+
clear() {
|
|
362
|
+
this.entries.length = 0;
|
|
363
|
+
this.activeStack.length = 0;
|
|
364
|
+
}
|
|
365
|
+
trim() {
|
|
366
|
+
if (this.entries.length > this.maxEntries) {
|
|
367
|
+
this.entries.splice(0, this.entries.length - this.maxEntries);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
}
|
|
302
371
|
export function releaseTuiInputStream(stream) {
|
|
303
372
|
if (stream.isTTY && typeof stream.setRawMode === 'function') {
|
|
304
373
|
stream.setRawMode(false);
|
|
305
374
|
}
|
|
306
375
|
stream.pause();
|
|
307
376
|
}
|
|
377
|
+
class MouseParser {
|
|
378
|
+
buffer = '';
|
|
379
|
+
feed(data, onEvent) {
|
|
380
|
+
this.buffer += data.toString('utf8');
|
|
381
|
+
while (this.buffer.length > 0) {
|
|
382
|
+
const mouseStart = this.buffer.indexOf('\u001b[<');
|
|
383
|
+
if (mouseStart === -1) {
|
|
384
|
+
if (this.buffer.length > 0) {
|
|
385
|
+
this.buffer = '';
|
|
386
|
+
}
|
|
387
|
+
break;
|
|
388
|
+
}
|
|
389
|
+
this.buffer = this.buffer.slice(mouseStart);
|
|
390
|
+
const parsed = this.tryParseMouse();
|
|
391
|
+
if (parsed === null) {
|
|
392
|
+
if (this.buffer.length > 256) {
|
|
393
|
+
this.buffer = '';
|
|
394
|
+
}
|
|
395
|
+
break;
|
|
396
|
+
}
|
|
397
|
+
onEvent(parsed);
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
tryParseMouse() {
|
|
401
|
+
const mIdx = this.buffer.indexOf('M', 3);
|
|
402
|
+
const mIdxSgr = this.buffer.indexOf('m', 3);
|
|
403
|
+
const end = mIdx !== -1 && (mIdxSgr === -1 || mIdx < mIdxSgr) ? mIdx : mIdxSgr;
|
|
404
|
+
if (end === -1)
|
|
405
|
+
return null;
|
|
406
|
+
const payload = this.buffer.slice(3, end);
|
|
407
|
+
const isRelease = this.buffer[end] === 'm';
|
|
408
|
+
this.buffer = this.buffer.slice(end + 1);
|
|
409
|
+
const semicolon = payload.indexOf(';');
|
|
410
|
+
if (semicolon === -1)
|
|
411
|
+
return null;
|
|
412
|
+
const secondSemicolon = payload.indexOf(';', semicolon + 1);
|
|
413
|
+
if (secondSemicolon === -1)
|
|
414
|
+
return null;
|
|
415
|
+
const cb = Number.parseInt(payload.slice(0, semicolon), 10);
|
|
416
|
+
const col = Number.parseInt(payload.slice(semicolon + 1, secondSemicolon), 10);
|
|
417
|
+
const row = Number.parseInt(payload.slice(secondSemicolon + 1), 10);
|
|
418
|
+
if (Number.isNaN(cb) || Number.isNaN(col) || Number.isNaN(row))
|
|
419
|
+
return null;
|
|
420
|
+
const shift = (cb & 4) !== 0;
|
|
421
|
+
const meta = (cb & 8) !== 0;
|
|
422
|
+
const ctrl = (cb & 16) !== 0;
|
|
423
|
+
const buttonCode = cb & 3;
|
|
424
|
+
if (isRelease) {
|
|
425
|
+
return { button: 'release', row, column: col, shift, ctrl, meta };
|
|
426
|
+
}
|
|
427
|
+
if ((cb & 64) !== 0) {
|
|
428
|
+
return { button: buttonCode === 0 ? 'wheel-up' : 'wheel-down', row, column: col, shift, ctrl, meta };
|
|
429
|
+
}
|
|
430
|
+
const isMotion = (cb & 32) !== 0;
|
|
431
|
+
if (isMotion) {
|
|
432
|
+
return { button: 'motion', row, column: col, shift, ctrl, meta };
|
|
433
|
+
}
|
|
434
|
+
const buttonMap = { 0: 'left', 1: 'middle', 2: 'right' };
|
|
435
|
+
return { button: buttonMap[buttonCode] ?? 'left', row, column: col, shift, ctrl, meta };
|
|
436
|
+
}
|
|
437
|
+
}
|
|
308
438
|
class FullscreenTui {
|
|
309
439
|
workspaceRoot;
|
|
310
440
|
options;
|
|
@@ -314,6 +444,7 @@ class FullscreenTui {
|
|
|
314
444
|
commands = [];
|
|
315
445
|
activityBySessionId = new Map();
|
|
316
446
|
ephemeralMessagesBySessionId = new Map();
|
|
447
|
+
taskTrackerBySessionId = new Map();
|
|
317
448
|
focusedPane = 'messages';
|
|
318
449
|
uiMode = 'command';
|
|
319
450
|
menuReturnMode = 'command';
|
|
@@ -327,16 +458,25 @@ class FullscreenTui {
|
|
|
327
458
|
sessions: 0,
|
|
328
459
|
messages: 0,
|
|
329
460
|
process: 0,
|
|
461
|
+
tasks: 0,
|
|
330
462
|
context: 0,
|
|
331
463
|
};
|
|
332
464
|
lastPaneMetrics = {
|
|
333
465
|
sessions: { maxStart: 0, viewportLines: 0, maxStartIsExact: true },
|
|
334
466
|
messages: { maxStart: 0, viewportLines: 0, maxStartIsExact: true },
|
|
335
467
|
process: { maxStart: 0, viewportLines: 0, maxStartIsExact: true },
|
|
468
|
+
tasks: { maxStart: 0, viewportLines: 0, maxStartIsExact: true },
|
|
336
469
|
context: { maxStart: 0, viewportLines: 0, maxStartIsExact: true },
|
|
337
470
|
};
|
|
338
471
|
currentPreview = null;
|
|
339
472
|
currentAbortController = null;
|
|
473
|
+
toolAbortController = null;
|
|
474
|
+
toolAbortSignalRef = {};
|
|
475
|
+
mouseParser = null;
|
|
476
|
+
mouseDataHandler = null;
|
|
477
|
+
mouseEscapeState = 'idle';
|
|
478
|
+
selection = null;
|
|
479
|
+
isDraggingSelection = false;
|
|
340
480
|
inputValue = '';
|
|
341
481
|
cursor = 0;
|
|
342
482
|
busy = false;
|
|
@@ -356,6 +496,8 @@ class FullscreenTui {
|
|
|
356
496
|
lastRenderAt = 0;
|
|
357
497
|
previousFrameLines = null;
|
|
358
498
|
previousFrameSize = null;
|
|
499
|
+
resizeHandler = null;
|
|
500
|
+
wrapCache = new Map();
|
|
359
501
|
constructor(workspaceRoot, options = {}) {
|
|
360
502
|
this.workspaceRoot = workspaceRoot;
|
|
361
503
|
this.options = options;
|
|
@@ -366,12 +508,22 @@ class FullscreenTui {
|
|
|
366
508
|
await this.refreshCommands();
|
|
367
509
|
await this.refreshSessions({ createIfMissing: true });
|
|
368
510
|
this.statusLine = `Ready. Shell ${this.getShellBackend().backendLabel} in ${path.relative(this.workspaceRoot, this.shellWorkspacePath) || '.'}. F10 or Ctrl+G opens menus. Left/right changes pane when the menu is closed.`;
|
|
369
|
-
emitKeypressEvents(input);
|
|
370
511
|
if (input.isTTY) {
|
|
371
512
|
input.setRawMode(true);
|
|
372
513
|
}
|
|
514
|
+
emitKeypressEvents(input);
|
|
373
515
|
input.on('keypress', this.onKeypress);
|
|
374
|
-
|
|
516
|
+
this.mouseParser = new MouseParser();
|
|
517
|
+
this.mouseDataHandler = (chunk) => {
|
|
518
|
+
this.mouseParser.feed(chunk, (event) => this.handleMouseEvent(event));
|
|
519
|
+
};
|
|
520
|
+
input.prependListener('data', this.mouseDataHandler);
|
|
521
|
+
this.resizeHandler = () => {
|
|
522
|
+
this.wrapCache.clear();
|
|
523
|
+
this.requestRender();
|
|
524
|
+
};
|
|
525
|
+
output.on('resize', this.resizeHandler);
|
|
526
|
+
output.write('\u001b[?1049h\u001b[?1002h\u001b[?1006h\u001b[2J\u001b[H');
|
|
375
527
|
this.requestRender();
|
|
376
528
|
await new Promise((resolve) => {
|
|
377
529
|
this.closeResolver = resolve;
|
|
@@ -386,16 +538,327 @@ class FullscreenTui {
|
|
|
386
538
|
clearInterval(this.animationTimer);
|
|
387
539
|
this.animationTimer = null;
|
|
388
540
|
}
|
|
389
|
-
|
|
541
|
+
if (this.mouseDataHandler) {
|
|
542
|
+
input.removeListener('data', this.mouseDataHandler);
|
|
543
|
+
this.mouseDataHandler = null;
|
|
544
|
+
}
|
|
545
|
+
this.mouseParser = null;
|
|
546
|
+
input.removeListener('keypress', this.onKeypress);
|
|
547
|
+
if (this.resizeHandler) {
|
|
548
|
+
output.off('resize', this.resizeHandler);
|
|
549
|
+
this.resizeHandler = null;
|
|
550
|
+
}
|
|
390
551
|
releaseTuiInputStream(input);
|
|
391
552
|
this.previousFrameLines = null;
|
|
392
553
|
this.previousFrameSize = null;
|
|
393
|
-
output.write('\u001b[?25h\u001b[?1049l');
|
|
554
|
+
output.write('\u001b[?25h\u001b[?1006l\u001b[?1002l\u001b[?1049l');
|
|
394
555
|
}
|
|
395
556
|
}
|
|
396
557
|
onKeypress = (text, key) => {
|
|
558
|
+
if (key.sequence === '\x1b[<') {
|
|
559
|
+
this.mouseEscapeState = 'consuming';
|
|
560
|
+
return;
|
|
561
|
+
}
|
|
562
|
+
if (this.mouseEscapeState === 'consuming') {
|
|
563
|
+
if (text === 'M' || text === 'm') {
|
|
564
|
+
this.mouseEscapeState = 'idle';
|
|
565
|
+
}
|
|
566
|
+
return;
|
|
567
|
+
}
|
|
397
568
|
void this.handleKeypress(text, key);
|
|
398
569
|
};
|
|
570
|
+
handleMouseEvent(event) {
|
|
571
|
+
if (event.button === 'wheel-up' || event.button === 'wheel-down') {
|
|
572
|
+
if (event.shift)
|
|
573
|
+
return;
|
|
574
|
+
const direction = event.button === 'wheel-up' ? 'up' : 'down';
|
|
575
|
+
const paneKind = this.paneKindAtPosition(event.row, event.column);
|
|
576
|
+
if (paneKind) {
|
|
577
|
+
this.focusedPane = paneKind;
|
|
578
|
+
}
|
|
579
|
+
this.scrollFocusedPane(direction, 3);
|
|
580
|
+
return;
|
|
581
|
+
}
|
|
582
|
+
if (event.button === 'left' && !event.shift && !event.ctrl && !event.meta) {
|
|
583
|
+
const menuIndex = this.menuIndexAtColumn(event.column);
|
|
584
|
+
if (menuIndex !== null && event.row === 1) {
|
|
585
|
+
this.clearSelection();
|
|
586
|
+
if (this.uiMode !== 'menu') {
|
|
587
|
+
this.uiMode = 'menu';
|
|
588
|
+
}
|
|
589
|
+
this.menuState.activeIndex = menuIndex;
|
|
590
|
+
this.menuState.selectedIndex = 0;
|
|
591
|
+
this.menuState.typeahead = '';
|
|
592
|
+
this.requestRender();
|
|
593
|
+
return;
|
|
594
|
+
}
|
|
595
|
+
if (this.uiMode === 'menu' && event.row >= 2) {
|
|
596
|
+
const menuItemIndex = this.menuItemAtRow(event.row);
|
|
597
|
+
if (menuItemIndex !== null) {
|
|
598
|
+
this.menuState.selectedIndex = menuItemIndex;
|
|
599
|
+
this.requestRender();
|
|
600
|
+
void this.activateCurrentMenuItem();
|
|
601
|
+
return;
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
const paneKind = this.paneKindAtPosition(event.row, event.column);
|
|
605
|
+
if (!paneKind) {
|
|
606
|
+
this.clearSelection();
|
|
607
|
+
this.requestRender();
|
|
608
|
+
return;
|
|
609
|
+
}
|
|
610
|
+
const panePos = this.paneContentPosition(paneKind, event.row, event.column);
|
|
611
|
+
if (!panePos) {
|
|
612
|
+
this.clearSelection();
|
|
613
|
+
this.focusedPane = paneKind;
|
|
614
|
+
this.requestRender();
|
|
615
|
+
return;
|
|
616
|
+
}
|
|
617
|
+
this.selection = { pane: paneKind, startRow: panePos.bodyRow, startCol: panePos.innerCol, endRow: panePos.bodyRow, endCol: panePos.innerCol };
|
|
618
|
+
this.isDraggingSelection = true;
|
|
619
|
+
this.focusedPane = paneKind;
|
|
620
|
+
this.requestRender();
|
|
621
|
+
return;
|
|
622
|
+
}
|
|
623
|
+
if (event.button === 'motion' && !event.shift && this.isDraggingSelection && this.selection) {
|
|
624
|
+
const paneKind = this.paneKindAtPosition(event.row, event.column);
|
|
625
|
+
if (paneKind && paneKind === this.selection.pane) {
|
|
626
|
+
const panePos = this.paneContentPosition(paneKind, event.row, event.column);
|
|
627
|
+
if (panePos) {
|
|
628
|
+
this.selection.endRow = panePos.bodyRow;
|
|
629
|
+
this.selection.endCol = panePos.innerCol;
|
|
630
|
+
this.requestRender();
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
return;
|
|
634
|
+
}
|
|
635
|
+
if (event.button === 'release' && !event.shift && this.isDraggingSelection) {
|
|
636
|
+
this.isDraggingSelection = false;
|
|
637
|
+
if (this.selection) {
|
|
638
|
+
const text = this.extractSelectionText();
|
|
639
|
+
if (text) {
|
|
640
|
+
this.copyToClipboard(text);
|
|
641
|
+
this.statusLine = 'Copied selection to clipboard.';
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
this.clearSelection();
|
|
645
|
+
this.requestRender();
|
|
646
|
+
return;
|
|
647
|
+
}
|
|
648
|
+
if (event.button === 'release') {
|
|
649
|
+
this.isDraggingSelection = false;
|
|
650
|
+
return;
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
clearSelection() {
|
|
654
|
+
if (this.selection) {
|
|
655
|
+
this.selection = null;
|
|
656
|
+
}
|
|
657
|
+
this.isDraggingSelection = false;
|
|
658
|
+
}
|
|
659
|
+
paneContentPosition(paneKind, screenRow, screenCol) {
|
|
660
|
+
const columns = output.columns ?? 120;
|
|
661
|
+
const rows = output.rows ?? 40;
|
|
662
|
+
if (screenRow < 2 || screenRow >= rows - 1)
|
|
663
|
+
return null;
|
|
664
|
+
if (this.uiMode !== 'command')
|
|
665
|
+
return null;
|
|
666
|
+
const contentHeight = rows - 3;
|
|
667
|
+
const widths = computePaneWidths(columns);
|
|
668
|
+
const rightHeights = computeRightPaneHeights(contentHeight);
|
|
669
|
+
const contentRow = screenRow - 2;
|
|
670
|
+
let paneLeft;
|
|
671
|
+
let paneWidth;
|
|
672
|
+
let paneTop;
|
|
673
|
+
switch (paneKind) {
|
|
674
|
+
case 'sessions':
|
|
675
|
+
paneLeft = 0;
|
|
676
|
+
paneWidth = widths.left;
|
|
677
|
+
paneTop = 0;
|
|
678
|
+
break;
|
|
679
|
+
case 'messages':
|
|
680
|
+
paneLeft = widths.left + 1;
|
|
681
|
+
paneWidth = widths.center;
|
|
682
|
+
paneTop = 0;
|
|
683
|
+
break;
|
|
684
|
+
case 'process':
|
|
685
|
+
paneLeft = widths.left + 1 + widths.center + 1;
|
|
686
|
+
paneWidth = widths.right;
|
|
687
|
+
paneTop = 0;
|
|
688
|
+
break;
|
|
689
|
+
case 'tasks':
|
|
690
|
+
paneLeft = widths.left + 1 + widths.center + 1;
|
|
691
|
+
paneWidth = widths.right;
|
|
692
|
+
paneTop = rightHeights.process;
|
|
693
|
+
break;
|
|
694
|
+
case 'context':
|
|
695
|
+
paneLeft = widths.left + 1 + widths.center + 1;
|
|
696
|
+
paneWidth = widths.right;
|
|
697
|
+
paneTop = rightHeights.process + rightHeights.tasks;
|
|
698
|
+
break;
|
|
699
|
+
}
|
|
700
|
+
if (screenCol < paneLeft + 2 || screenCol > paneLeft + paneWidth - 1)
|
|
701
|
+
return null;
|
|
702
|
+
const bodyRow = contentRow - paneTop - 1;
|
|
703
|
+
if (bodyRow < 0)
|
|
704
|
+
return null;
|
|
705
|
+
const innerCol = screenCol - paneLeft - 2;
|
|
706
|
+
return { bodyRow, innerCol };
|
|
707
|
+
}
|
|
708
|
+
extractSelectionText() {
|
|
709
|
+
if (!this.selection)
|
|
710
|
+
return '';
|
|
711
|
+
const sel = this.selection;
|
|
712
|
+
const paneBody = this.getPaneBody(sel.pane);
|
|
713
|
+
const columns = output.columns ?? 120;
|
|
714
|
+
const widths = computePaneWidths(columns);
|
|
715
|
+
const width = sel.pane === 'sessions' ? widths.left : sel.pane === 'messages' ? widths.center : widths.right;
|
|
716
|
+
const innerWidth = Math.max(1, width - 2);
|
|
717
|
+
const wrappedLines = wrapBodyLines(paneBody, innerWidth);
|
|
718
|
+
const startRow = Math.min(sel.startRow, sel.endRow);
|
|
719
|
+
const endRow = Math.max(sel.startRow, sel.endRow);
|
|
720
|
+
const startCol = sel.startRow <= sel.endRow ? sel.startCol : sel.endCol;
|
|
721
|
+
const endCol = sel.startRow <= sel.endRow ? sel.endCol : sel.startCol;
|
|
722
|
+
if (startRow === endRow) {
|
|
723
|
+
const line = wrappedLines[startRow] ?? '';
|
|
724
|
+
return this.stripAnsi(line.slice(startCol, endCol + 1));
|
|
725
|
+
}
|
|
726
|
+
const parts = [];
|
|
727
|
+
const firstLine = wrappedLines[startRow] ?? '';
|
|
728
|
+
parts.push(this.stripAnsi(firstLine.slice(startCol)));
|
|
729
|
+
for (let row = startRow + 1; row < endRow; row++) {
|
|
730
|
+
parts.push(this.stripAnsi(wrappedLines[row] ?? ''));
|
|
731
|
+
}
|
|
732
|
+
const lastLine = wrappedLines[endRow] ?? '';
|
|
733
|
+
parts.push(this.stripAnsi(lastLine.slice(0, endCol + 1)));
|
|
734
|
+
return parts.join('\n');
|
|
735
|
+
}
|
|
736
|
+
stripAnsi(text) {
|
|
737
|
+
return text.replace(/\u001b\[[0-9;]*m/g, '');
|
|
738
|
+
}
|
|
739
|
+
copyToClipboard(text) {
|
|
740
|
+
const osc52 = `\u001b]52;c;${Buffer.from(text).toString('base64')}\u0007`;
|
|
741
|
+
output.write(osc52);
|
|
742
|
+
}
|
|
743
|
+
applySelectionHighlight(lines, widths, rightHeights, _contentHeight) {
|
|
744
|
+
const sel = this.selection;
|
|
745
|
+
if (!sel)
|
|
746
|
+
return;
|
|
747
|
+
const startRow = Math.min(sel.startRow, sel.endRow);
|
|
748
|
+
const endRow = Math.max(sel.startRow, sel.endRow);
|
|
749
|
+
const startCol = sel.startRow <= sel.endRow ? sel.startCol : sel.endCol;
|
|
750
|
+
const endCol = sel.startRow <= sel.endRow ? sel.endCol : sel.startCol;
|
|
751
|
+
let paneLeft;
|
|
752
|
+
let paneWidth;
|
|
753
|
+
let paneTop;
|
|
754
|
+
switch (sel.pane) {
|
|
755
|
+
case 'sessions':
|
|
756
|
+
paneLeft = 0;
|
|
757
|
+
paneWidth = widths.left;
|
|
758
|
+
paneTop = 0;
|
|
759
|
+
break;
|
|
760
|
+
case 'messages':
|
|
761
|
+
paneLeft = widths.left + 1;
|
|
762
|
+
paneWidth = widths.center;
|
|
763
|
+
paneTop = 0;
|
|
764
|
+
break;
|
|
765
|
+
case 'process':
|
|
766
|
+
paneLeft = widths.left + 1 + widths.center + 1;
|
|
767
|
+
paneWidth = widths.right;
|
|
768
|
+
paneTop = 0;
|
|
769
|
+
break;
|
|
770
|
+
case 'tasks':
|
|
771
|
+
paneLeft = widths.left + 1 + widths.center + 1;
|
|
772
|
+
paneWidth = widths.right;
|
|
773
|
+
paneTop = rightHeights.process;
|
|
774
|
+
break;
|
|
775
|
+
case 'context':
|
|
776
|
+
paneLeft = widths.left + 1 + widths.center + 1;
|
|
777
|
+
paneWidth = widths.right;
|
|
778
|
+
paneTop = rightHeights.process + rightHeights.tasks;
|
|
779
|
+
break;
|
|
780
|
+
}
|
|
781
|
+
const innerDisplayStart = paneLeft + 1;
|
|
782
|
+
const innerDisplayEnd = paneLeft + paneWidth - 2;
|
|
783
|
+
for (let bodyRow = startRow; bodyRow <= endRow; bodyRow++) {
|
|
784
|
+
const screenLineIdx = 1 + paneTop + 1 + bodyRow;
|
|
785
|
+
if (screenLineIdx >= lines.length)
|
|
786
|
+
break;
|
|
787
|
+
const line = lines[screenLineIdx];
|
|
788
|
+
const rowStartCol = bodyRow === startRow ? innerDisplayStart + startCol : innerDisplayStart;
|
|
789
|
+
const rowEndCol = bodyRow === endRow ? innerDisplayStart + endCol + 1 : innerDisplayEnd + 1;
|
|
790
|
+
const clampedStartCol = Math.max(innerDisplayStart, rowStartCol);
|
|
791
|
+
const clampedEndCol = Math.min(innerDisplayEnd + 1, rowEndCol);
|
|
792
|
+
if (clampedStartCol >= clampedEndCol)
|
|
793
|
+
continue;
|
|
794
|
+
const strStart = displayColumnToStringIndex(line, clampedStartCol);
|
|
795
|
+
const strEnd = displayColumnToStringIndex(line, clampedEndCol);
|
|
796
|
+
const before = line.slice(0, strStart);
|
|
797
|
+
const segment = line.slice(strStart, strEnd);
|
|
798
|
+
const after = line.slice(strEnd);
|
|
799
|
+
const highlighted = this.invertAnsiColors(segment);
|
|
800
|
+
lines[screenLineIdx] = `${before}${highlighted}${after}`;
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
invertAnsiColors(text) {
|
|
804
|
+
return `\u001b[7m${text}\u001b[27m`;
|
|
805
|
+
}
|
|
806
|
+
paneKindAtPosition(row, column) {
|
|
807
|
+
const columns = output.columns ?? 120;
|
|
808
|
+
const rows = output.rows ?? 40;
|
|
809
|
+
if (row < 2 || row >= rows - 1)
|
|
810
|
+
return null;
|
|
811
|
+
if (this.uiMode !== 'command')
|
|
812
|
+
return null;
|
|
813
|
+
const widths = computePaneWidths(columns);
|
|
814
|
+
const rightHeights = computeRightPaneHeights(rows - 3);
|
|
815
|
+
const contentRow = row - 1;
|
|
816
|
+
if (column <= widths.left)
|
|
817
|
+
return 'sessions';
|
|
818
|
+
if (column <= widths.left + 1 + widths.center)
|
|
819
|
+
return 'messages';
|
|
820
|
+
if (contentRow <= rightHeights.process)
|
|
821
|
+
return 'process';
|
|
822
|
+
if (contentRow <= rightHeights.process + rightHeights.tasks)
|
|
823
|
+
return 'tasks';
|
|
824
|
+
return 'context';
|
|
825
|
+
}
|
|
826
|
+
menuIndexAtColumn(column) {
|
|
827
|
+
const menuLabels = ['Session', 'Skills', 'Files', 'Run', 'View', 'Help'];
|
|
828
|
+
let pos = 1 + measureDisplayWidth(' DeepClause ');
|
|
829
|
+
for (let i = 0; i < menuLabels.length; i++) {
|
|
830
|
+
const labelWidth = measureDisplayWidth(` [${i + 1}] ${menuLabels[i]} `);
|
|
831
|
+
if (column >= pos && column < pos + labelWidth)
|
|
832
|
+
return i;
|
|
833
|
+
pos += labelWidth;
|
|
834
|
+
}
|
|
835
|
+
return null;
|
|
836
|
+
}
|
|
837
|
+
menuItemAtRow(row) {
|
|
838
|
+
if (this.uiMode !== 'menu')
|
|
839
|
+
return null;
|
|
840
|
+
const contentRow = row - 2;
|
|
841
|
+
if (contentRow < 2)
|
|
842
|
+
return null;
|
|
843
|
+
const itemRow = contentRow - 2;
|
|
844
|
+
if (itemRow < 0)
|
|
845
|
+
return null;
|
|
846
|
+
const linesPerItem = 3;
|
|
847
|
+
const itemIndex = Math.floor(itemRow / linesPerItem);
|
|
848
|
+
const menu = this.getMenuDefinitions()[this.menuState.activeIndex];
|
|
849
|
+
if (!menu || itemIndex >= menu.items.length || itemIndex < 0)
|
|
850
|
+
return null;
|
|
851
|
+
return itemIndex;
|
|
852
|
+
}
|
|
853
|
+
async activateCurrentMenuItem() {
|
|
854
|
+
const menu = this.getMenuDefinitions()[this.menuState.activeIndex];
|
|
855
|
+
if (!menu)
|
|
856
|
+
return;
|
|
857
|
+
const item = menu.items[this.menuState.selectedIndex];
|
|
858
|
+
if (!item || item.enabled === false)
|
|
859
|
+
return;
|
|
860
|
+
await this.runMenuAction(item.id);
|
|
861
|
+
}
|
|
399
862
|
async handleKeypress(text, key) {
|
|
400
863
|
if ((key.ctrl && key.name === 'g') || key.name === 'f10') {
|
|
401
864
|
this.toggleMenuMode();
|
|
@@ -428,6 +891,19 @@ class FullscreenTui {
|
|
|
428
891
|
this.close();
|
|
429
892
|
return;
|
|
430
893
|
}
|
|
894
|
+
if (key.ctrl && key.name === 't' && this.busy) {
|
|
895
|
+
if (this.toolAbortController && !this.toolAbortController.signal.aborted) {
|
|
896
|
+
this.toolAbortController.abort('Aborted by user');
|
|
897
|
+
this.toolAbortController = null;
|
|
898
|
+
this.toolAbortSignalRef.signal = undefined;
|
|
899
|
+
this.statusLine = 'Tool execution cancelled.';
|
|
900
|
+
}
|
|
901
|
+
else {
|
|
902
|
+
this.statusLine = 'No active tool to cancel.';
|
|
903
|
+
}
|
|
904
|
+
this.requestRender();
|
|
905
|
+
return;
|
|
906
|
+
}
|
|
431
907
|
if (this.uiMode === 'menu') {
|
|
432
908
|
await this.handleMenuKeypress(text, key);
|
|
433
909
|
return;
|
|
@@ -460,7 +936,7 @@ class FullscreenTui {
|
|
|
460
936
|
return;
|
|
461
937
|
}
|
|
462
938
|
if (this.cursor > 0) {
|
|
463
|
-
this.cursor
|
|
939
|
+
this.cursor = this.graphemeLeftIndex(this.inputValue, this.cursor);
|
|
464
940
|
}
|
|
465
941
|
break;
|
|
466
942
|
case 'right':
|
|
@@ -469,7 +945,7 @@ class FullscreenTui {
|
|
|
469
945
|
return;
|
|
470
946
|
}
|
|
471
947
|
if (this.cursor < this.inputValue.length) {
|
|
472
|
-
this.cursor
|
|
948
|
+
this.cursor = this.graphemeRightIndex(this.inputValue, this.cursor);
|
|
473
949
|
}
|
|
474
950
|
break;
|
|
475
951
|
case 'home':
|
|
@@ -642,13 +1118,13 @@ class FullscreenTui {
|
|
|
642
1118
|
return;
|
|
643
1119
|
case 'left':
|
|
644
1120
|
if (this.cursor > 0) {
|
|
645
|
-
this.cursor
|
|
1121
|
+
this.cursor = this.graphemeLeftIndex(this.inputValue, this.cursor);
|
|
646
1122
|
this.requestRender();
|
|
647
1123
|
}
|
|
648
1124
|
return;
|
|
649
1125
|
case 'right':
|
|
650
1126
|
if (this.cursor < this.inputValue.length) {
|
|
651
|
-
this.cursor
|
|
1127
|
+
this.cursor = this.graphemeRightIndex(this.inputValue, this.cursor);
|
|
652
1128
|
this.requestRender();
|
|
653
1129
|
}
|
|
654
1130
|
return;
|
|
@@ -875,6 +1351,8 @@ class FullscreenTui {
|
|
|
875
1351
|
activity.pushLine(`task ${promptText}`);
|
|
876
1352
|
this.busy = true;
|
|
877
1353
|
this.currentAbortController = new AbortController();
|
|
1354
|
+
this.toolAbortController = new AbortController();
|
|
1355
|
+
this.toolAbortSignalRef.signal = this.toolAbortController.signal;
|
|
878
1356
|
this.startAnimation();
|
|
879
1357
|
this.statusLine = 'Running conductor...';
|
|
880
1358
|
this.requestRender();
|
|
@@ -886,9 +1364,12 @@ class FullscreenTui {
|
|
|
886
1364
|
headless: true,
|
|
887
1365
|
sandbox: this.options.sandbox,
|
|
888
1366
|
signal: this.currentAbortController.signal,
|
|
1367
|
+
toolAbortSignalRef: this.toolAbortSignalRef,
|
|
889
1368
|
onUserInput: (question) => this.requestClarification(question),
|
|
890
1369
|
onEvent: (event) => {
|
|
1370
|
+
this.manageToolAbortSignal(event.event);
|
|
891
1371
|
activity.handle(event);
|
|
1372
|
+
this.taskTrackerFor(sessionId).handle(event);
|
|
892
1373
|
this.updatePreviewFromEvent(event);
|
|
893
1374
|
this.requestRender();
|
|
894
1375
|
},
|
|
@@ -912,6 +1393,8 @@ class FullscreenTui {
|
|
|
912
1393
|
finally {
|
|
913
1394
|
this.busy = false;
|
|
914
1395
|
this.currentAbortController = null;
|
|
1396
|
+
this.toolAbortController = null;
|
|
1397
|
+
this.toolAbortSignalRef.signal = undefined;
|
|
915
1398
|
this.stopAnimation();
|
|
916
1399
|
if (this.exitRequested) {
|
|
917
1400
|
this.close();
|
|
@@ -933,6 +1416,8 @@ class FullscreenTui {
|
|
|
933
1416
|
activity.pushLine(`skill /${skillName}${args.length > 0 ? ` ${args.join(' ')}` : ''}`);
|
|
934
1417
|
this.busy = true;
|
|
935
1418
|
this.currentAbortController = new AbortController();
|
|
1419
|
+
this.toolAbortController = new AbortController();
|
|
1420
|
+
this.toolAbortSignalRef.signal = this.toolAbortController.signal;
|
|
936
1421
|
this.startAnimation();
|
|
937
1422
|
this.statusLine = `Running /${skillName}...`;
|
|
938
1423
|
this.requestRender();
|
|
@@ -941,14 +1426,18 @@ class FullscreenTui {
|
|
|
941
1426
|
sessionId: this.selectedSessionId || undefined,
|
|
942
1427
|
sandbox: this.options.sandbox,
|
|
943
1428
|
signal: this.currentAbortController.signal,
|
|
1429
|
+
toolAbortSignalRef: this.toolAbortSignalRef,
|
|
944
1430
|
onUserInput: (question) => this.requestClarification(question),
|
|
945
1431
|
onEvent: (event) => {
|
|
1432
|
+
this.manageToolAbortSignal(event.event);
|
|
946
1433
|
activity.handle(event);
|
|
1434
|
+
this.taskTrackerFor(sessionId).handle(event);
|
|
947
1435
|
this.updatePreviewFromEvent(event);
|
|
948
1436
|
this.requestRender();
|
|
949
1437
|
},
|
|
950
1438
|
});
|
|
951
1439
|
activity.finish();
|
|
1440
|
+
await this.refreshSessions({ selectedSessionId: this.selectedSessionId });
|
|
952
1441
|
await this.refreshCommands();
|
|
953
1442
|
this.finishExecutionPreview({
|
|
954
1443
|
persist: true,
|
|
@@ -967,6 +1456,8 @@ class FullscreenTui {
|
|
|
967
1456
|
finally {
|
|
968
1457
|
this.busy = false;
|
|
969
1458
|
this.currentAbortController = null;
|
|
1459
|
+
this.toolAbortController = null;
|
|
1460
|
+
this.toolAbortSignalRef.signal = undefined;
|
|
970
1461
|
this.stopAnimation();
|
|
971
1462
|
if (this.exitRequested) {
|
|
972
1463
|
this.close();
|
|
@@ -995,6 +1486,8 @@ class FullscreenTui {
|
|
|
995
1486
|
activity.pushLine(`run ${commandText}`);
|
|
996
1487
|
this.busy = true;
|
|
997
1488
|
this.currentAbortController = new AbortController();
|
|
1489
|
+
this.toolAbortController = new AbortController();
|
|
1490
|
+
this.toolAbortSignalRef.signal = this.toolAbortController.signal;
|
|
998
1491
|
this.startAnimation();
|
|
999
1492
|
this.statusLine = `Running ${commandText}...`;
|
|
1000
1493
|
this.requestRender();
|
|
@@ -1003,14 +1496,18 @@ class FullscreenTui {
|
|
|
1003
1496
|
sessionId: this.selectedSessionId || undefined,
|
|
1004
1497
|
sandbox: this.options.sandbox,
|
|
1005
1498
|
signal: this.currentAbortController.signal,
|
|
1499
|
+
toolAbortSignalRef: this.toolAbortSignalRef,
|
|
1006
1500
|
onUserInput: (question) => this.requestClarification(question),
|
|
1007
1501
|
onEvent: (event) => {
|
|
1502
|
+
this.manageToolAbortSignal(event.event);
|
|
1008
1503
|
activity.handle(event);
|
|
1504
|
+
this.taskTrackerFor(sessionId).handle(event);
|
|
1009
1505
|
this.updatePreviewFromEvent(event);
|
|
1010
1506
|
this.requestRender();
|
|
1011
1507
|
},
|
|
1012
1508
|
});
|
|
1013
1509
|
activity.finish();
|
|
1510
|
+
await this.refreshSessions({ selectedSessionId: this.selectedSessionId });
|
|
1014
1511
|
await this.refreshCommands();
|
|
1015
1512
|
this.finishExecutionPreview({
|
|
1016
1513
|
persist: true,
|
|
@@ -1029,6 +1526,8 @@ class FullscreenTui {
|
|
|
1029
1526
|
finally {
|
|
1030
1527
|
this.busy = false;
|
|
1031
1528
|
this.currentAbortController = null;
|
|
1529
|
+
this.toolAbortController = null;
|
|
1530
|
+
this.toolAbortSignalRef.signal = undefined;
|
|
1032
1531
|
this.stopAnimation();
|
|
1033
1532
|
if (this.exitRequested) {
|
|
1034
1533
|
this.close();
|
|
@@ -1056,6 +1555,8 @@ class FullscreenTui {
|
|
|
1056
1555
|
activity.pushLine(`skill ${commandText}`);
|
|
1057
1556
|
this.busy = true;
|
|
1058
1557
|
this.currentAbortController = new AbortController();
|
|
1558
|
+
this.toolAbortController = new AbortController();
|
|
1559
|
+
this.toolAbortSignalRef.signal = this.toolAbortController.signal;
|
|
1059
1560
|
this.startAnimation();
|
|
1060
1561
|
this.statusLine = `Running /${commandName}...`;
|
|
1061
1562
|
this.requestRender();
|
|
@@ -1065,12 +1566,15 @@ class FullscreenTui {
|
|
|
1065
1566
|
signal: this.currentAbortController.signal,
|
|
1066
1567
|
onUserInput: (question) => this.requestClarification(question),
|
|
1067
1568
|
onEvent: (event) => {
|
|
1569
|
+
this.manageToolAbortSignal(event.event);
|
|
1068
1570
|
activity.handle(event);
|
|
1571
|
+
this.taskTrackerFor(sessionId).handle(event);
|
|
1069
1572
|
this.updatePreviewFromEvent(event);
|
|
1070
1573
|
this.requestRender();
|
|
1071
1574
|
},
|
|
1072
1575
|
});
|
|
1073
1576
|
activity.finish();
|
|
1577
|
+
await this.refreshSessions({ selectedSessionId: this.selectedSessionId });
|
|
1074
1578
|
await this.refreshCommands();
|
|
1075
1579
|
this.finishExecutionPreview({
|
|
1076
1580
|
persist: true,
|
|
@@ -1088,6 +1592,8 @@ class FullscreenTui {
|
|
|
1088
1592
|
finally {
|
|
1089
1593
|
this.busy = false;
|
|
1090
1594
|
this.currentAbortController = null;
|
|
1595
|
+
this.toolAbortController = null;
|
|
1596
|
+
this.toolAbortSignalRef.signal = undefined;
|
|
1091
1597
|
this.stopAnimation();
|
|
1092
1598
|
if (this.exitRequested) {
|
|
1093
1599
|
this.close();
|
|
@@ -1119,6 +1625,8 @@ class FullscreenTui {
|
|
|
1119
1625
|
activity.pushLine(`shell ${persistOutput ? '!!' : '!'}${trimmedCommand}`);
|
|
1120
1626
|
this.busy = true;
|
|
1121
1627
|
this.currentAbortController = new AbortController();
|
|
1628
|
+
this.toolAbortController = this.currentAbortController;
|
|
1629
|
+
this.toolAbortSignalRef.signal = this.toolAbortController.signal;
|
|
1122
1630
|
this.startAnimation();
|
|
1123
1631
|
this.statusLine = `Running ${persistOutput ? '!!' : '!'}${trimmedCommand}...`;
|
|
1124
1632
|
this.requestRender();
|
|
@@ -1129,6 +1637,7 @@ class FullscreenTui {
|
|
|
1129
1637
|
signal: this.currentAbortController.signal,
|
|
1130
1638
|
onEvent: (event) => {
|
|
1131
1639
|
activity.handle(event);
|
|
1640
|
+
this.taskTrackerFor(sessionId).handle(event);
|
|
1132
1641
|
this.requestRender();
|
|
1133
1642
|
},
|
|
1134
1643
|
});
|
|
@@ -1163,6 +1672,8 @@ class FullscreenTui {
|
|
|
1163
1672
|
finally {
|
|
1164
1673
|
this.busy = false;
|
|
1165
1674
|
this.currentAbortController = null;
|
|
1675
|
+
this.toolAbortController = null;
|
|
1676
|
+
this.toolAbortSignalRef.signal = undefined;
|
|
1166
1677
|
this.stopAnimation();
|
|
1167
1678
|
if (this.exitRequested) {
|
|
1168
1679
|
this.close();
|
|
@@ -1247,7 +1758,7 @@ class FullscreenTui {
|
|
|
1247
1758
|
this.scrollFocusedPane(offset < 0 ? 'up' : 'down', 1);
|
|
1248
1759
|
}
|
|
1249
1760
|
cyclePaneFocus(direction) {
|
|
1250
|
-
const order = ['sessions', 'messages', 'process', 'context'];
|
|
1761
|
+
const order = ['sessions', 'messages', 'process', 'tasks', 'context'];
|
|
1251
1762
|
const currentIndex = order.indexOf(this.focusedPane);
|
|
1252
1763
|
const nextIndex = (currentIndex + direction + order.length) % order.length;
|
|
1253
1764
|
this.focusedPane = order[nextIndex];
|
|
@@ -1369,6 +1880,7 @@ class FullscreenTui {
|
|
|
1369
1880
|
{ id: 'view.focus.sessions', label: 'Focus Sessions Pane', description: 'Focus the session list pane.' },
|
|
1370
1881
|
{ id: 'view.focus.messages', label: 'Focus Messages Pane', description: 'Focus the conversation history pane.' },
|
|
1371
1882
|
{ id: 'view.focus.process', label: 'Focus Execution Pane', description: 'Focus the live execution log pane.' },
|
|
1883
|
+
{ id: 'view.focus.tasks', label: 'Focus Tasks Pane', description: 'Focus the step tracker pane.' },
|
|
1372
1884
|
{ id: 'view.focus.context', label: 'Focus Context Pane', description: 'Focus the session context and token usage pane.' },
|
|
1373
1885
|
{ id: 'view.follow', label: 'Follow Focused Pane', description: 'Jump back to the newest visible content in the active pane.' },
|
|
1374
1886
|
{ id: 'view.palette', label: 'Open Action Palette', description: 'Search all menu actions from one picker.', shortcut: 'Ctrl+P' },
|
|
@@ -1486,6 +1998,7 @@ class FullscreenTui {
|
|
|
1486
1998
|
case 'view.focus.sessions':
|
|
1487
1999
|
case 'view.focus.messages':
|
|
1488
2000
|
case 'view.focus.process':
|
|
2001
|
+
case 'view.focus.tasks':
|
|
1489
2002
|
case 'view.focus.context': {
|
|
1490
2003
|
const pane = actionId.slice('view.focus.'.length);
|
|
1491
2004
|
this.focusedPane = pane;
|
|
@@ -2355,6 +2868,36 @@ class FullscreenTui {
|
|
|
2355
2868
|
currentProcessActivity() {
|
|
2356
2869
|
return this.activityFor(this.selectedSessionId || 'global');
|
|
2357
2870
|
}
|
|
2871
|
+
graphemeLeftIndex(text, index) {
|
|
2872
|
+
if (index <= 0)
|
|
2873
|
+
return 0;
|
|
2874
|
+
if (!graphemeSegmenter)
|
|
2875
|
+
return index - 1;
|
|
2876
|
+
const graphemes = Array.from(graphemeSegmenter.segment(text), (g) => g.segment);
|
|
2877
|
+
let pos = 0;
|
|
2878
|
+
for (const g of graphemes) {
|
|
2879
|
+
if (pos + g.length >= index)
|
|
2880
|
+
break;
|
|
2881
|
+
pos += g.length;
|
|
2882
|
+
}
|
|
2883
|
+
return pos;
|
|
2884
|
+
}
|
|
2885
|
+
graphemeRightIndex(text, index) {
|
|
2886
|
+
if (index >= text.length)
|
|
2887
|
+
return text.length;
|
|
2888
|
+
if (!graphemeSegmenter)
|
|
2889
|
+
return index + 1;
|
|
2890
|
+
const graphemes = Array.from(graphemeSegmenter.segment(text), (g) => g.segment);
|
|
2891
|
+
let pos = 0;
|
|
2892
|
+
for (const g of graphemes) {
|
|
2893
|
+
if (pos >= index) {
|
|
2894
|
+
pos += g.length;
|
|
2895
|
+
break;
|
|
2896
|
+
}
|
|
2897
|
+
pos += g.length;
|
|
2898
|
+
}
|
|
2899
|
+
return Math.min(pos, text.length);
|
|
2900
|
+
}
|
|
2358
2901
|
insertText(text) {
|
|
2359
2902
|
const previousInput = this.inputValue;
|
|
2360
2903
|
this.inputValue = `${this.inputValue.slice(0, this.cursor)}${text}${this.inputValue.slice(this.cursor)}`;
|
|
@@ -2366,8 +2909,9 @@ class FullscreenTui {
|
|
|
2366
2909
|
return;
|
|
2367
2910
|
}
|
|
2368
2911
|
const previousInput = this.inputValue;
|
|
2369
|
-
|
|
2370
|
-
this.
|
|
2912
|
+
const deleteFrom = this.graphemeLeftIndex(this.inputValue, this.cursor);
|
|
2913
|
+
this.inputValue = `${this.inputValue.slice(0, deleteFrom)}${this.inputValue.slice(this.cursor)}`;
|
|
2914
|
+
this.cursor = deleteFrom;
|
|
2371
2915
|
this.refreshCommandBarStatus(previousInput);
|
|
2372
2916
|
}
|
|
2373
2917
|
deleteForward() {
|
|
@@ -2448,6 +2992,14 @@ class FullscreenTui {
|
|
|
2448
2992
|
this.activityBySessionId.set(key, created);
|
|
2449
2993
|
return created;
|
|
2450
2994
|
}
|
|
2995
|
+
taskTrackerFor(sessionId) {
|
|
2996
|
+
let tracker = this.taskTrackerBySessionId.get(sessionId);
|
|
2997
|
+
if (!tracker) {
|
|
2998
|
+
tracker = new TaskTracker();
|
|
2999
|
+
this.taskTrackerBySessionId.set(sessionId, tracker);
|
|
3000
|
+
}
|
|
3001
|
+
return tracker;
|
|
3002
|
+
}
|
|
2451
3003
|
async persistShellCommandResult(command, result) {
|
|
2452
3004
|
let sessionId = this.selectedSessionId;
|
|
2453
3005
|
if (!sessionId) {
|
|
@@ -2485,6 +3037,7 @@ class FullscreenTui {
|
|
|
2485
3037
|
const sessionsPane = this.buildPaneView('sessions', 'Sessions', widths.left, contentHeight);
|
|
2486
3038
|
const messagesPane = this.buildPaneView('messages', 'Messages', widths.center, contentHeight);
|
|
2487
3039
|
const processPane = this.buildPaneView('process', 'Execution', widths.right, rightHeights.process);
|
|
3040
|
+
const tasksPane = this.buildPaneView('tasks', 'Tasks', widths.right, rightHeights.tasks);
|
|
2488
3041
|
const contextPane = this.buildPaneView('context', 'Context', widths.right, rightHeights.context);
|
|
2489
3042
|
const workspaceView = this.uiMode === 'command'
|
|
2490
3043
|
? null
|
|
@@ -2501,31 +3054,43 @@ class FullscreenTui {
|
|
|
2501
3054
|
}
|
|
2502
3055
|
else {
|
|
2503
3056
|
for (let index = 0; index < contentHeight; index += 1) {
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
3057
|
+
let rightLine;
|
|
3058
|
+
if (index < rightHeights.process) {
|
|
3059
|
+
rightLine = processPane[index];
|
|
3060
|
+
}
|
|
3061
|
+
else if (index < rightHeights.process + rightHeights.tasks) {
|
|
3062
|
+
rightLine = tasksPane[index - rightHeights.process];
|
|
3063
|
+
}
|
|
3064
|
+
else {
|
|
3065
|
+
rightLine = contextPane[index - rightHeights.process - rightHeights.tasks];
|
|
3066
|
+
}
|
|
2507
3067
|
lines.push(`${sessionsPane[index]}${gap}${messagesPane[index]}${gap}${rightLine}`);
|
|
2508
3068
|
}
|
|
2509
3069
|
}
|
|
2510
3070
|
lines.push(status);
|
|
2511
3071
|
lines.push(inputLine);
|
|
3072
|
+
if (this.selection) {
|
|
3073
|
+
this.applySelectionHighlight(lines, widths, rightHeights, contentHeight);
|
|
3074
|
+
}
|
|
2512
3075
|
this.paintFrame(lines, { columns, rows });
|
|
2513
3076
|
if (workspaceView?.cursor && !this.pendingPrompt) {
|
|
2514
3077
|
output.write(`\u001b[${workspaceView.cursor.row};${workspaceView.cursor.column}H`);
|
|
2515
3078
|
return;
|
|
2516
3079
|
}
|
|
2517
|
-
const
|
|
3080
|
+
const textBeforeCursor = this.inputValue.slice(0, this.cursor);
|
|
3081
|
+
const cursorColumn = Math.min(columns, inputPrefix.length + measureDisplayWidth(textBeforeCursor) + 1);
|
|
2518
3082
|
output.write(`\u001b[${rows};${cursorColumn}H`);
|
|
2519
3083
|
}
|
|
2520
3084
|
paintFrame(lines, size) {
|
|
2521
|
-
const
|
|
3085
|
+
const paddedLines = lines.map((line) => padRight(line, size.columns));
|
|
3086
|
+
const patch = computeFramePatch(this.previousFrameLines, paddedLines, this.previousFrameSize, size);
|
|
2522
3087
|
let buffer = '\u001b[0m';
|
|
2523
3088
|
if (patch.fullRender) {
|
|
2524
|
-
buffer += `\u001b[H
|
|
3089
|
+
buffer += `\u001b[H${paddedLines.join('\n')}\u001b[?25h`;
|
|
2525
3090
|
}
|
|
2526
3091
|
else if (patch.changedRows.length > 0) {
|
|
2527
3092
|
for (const change of patch.changedRows) {
|
|
2528
|
-
buffer += `\u001b[${change.row};1H
|
|
3093
|
+
buffer += `\u001b[${change.row};1H${change.line}`;
|
|
2529
3094
|
}
|
|
2530
3095
|
buffer += '\u001b[?25h';
|
|
2531
3096
|
}
|
|
@@ -2533,7 +3098,7 @@ class FullscreenTui {
|
|
|
2533
3098
|
buffer += '\u001b[?25h';
|
|
2534
3099
|
}
|
|
2535
3100
|
output.write(buffer);
|
|
2536
|
-
this.previousFrameLines = [...
|
|
3101
|
+
this.previousFrameLines = [...paddedLines];
|
|
2537
3102
|
this.previousFrameSize = size;
|
|
2538
3103
|
}
|
|
2539
3104
|
buildHeaderLine() {
|
|
@@ -2547,32 +3112,35 @@ class FullscreenTui {
|
|
|
2547
3112
|
return `${sessionLabel} ${mode} ${skillLabel}`;
|
|
2548
3113
|
}
|
|
2549
3114
|
buildStatusLine() {
|
|
3115
|
+
const spinner = this.busy || this.currentPreview?.entries.some((entry) => entry.pending)
|
|
3116
|
+
? ` ${currentSpinnerFrame()}`
|
|
3117
|
+
: '';
|
|
2550
3118
|
if (this.uiMode === 'menu') {
|
|
2551
3119
|
const menu = this.getMenuDefinitions()[this.menuState.activeIndex];
|
|
2552
|
-
return
|
|
3120
|
+
return `${spinner} Menu ${menu?.label ?? ''} | ${menu?.items[this.menuState.selectedIndex]?.description ?? 'Choose an action.'}`;
|
|
2553
3121
|
}
|
|
2554
3122
|
if ((this.uiMode === 'picker' || this.uiMode === 'palette') && this.pickerState) {
|
|
2555
3123
|
const selected = this.pickerState.filteredItems[this.pickerState.selectedIndex];
|
|
2556
|
-
return `${this.pickerState.title} | ${selected?.description ?? this.pickerState.emptyText}`;
|
|
3124
|
+
return `${spinner} ${this.pickerState.title} | ${selected?.description ?? this.pickerState.emptyText}`;
|
|
2557
3125
|
}
|
|
2558
3126
|
if (this.uiMode === 'viewer' && this.viewerState) {
|
|
2559
|
-
return `${this.viewerState.title} | ${this.viewerState.path ? path.relative(this.workspaceRoot, this.viewerState.path) : 'read-only view'}`;
|
|
3127
|
+
return `${spinner} ${this.viewerState.title} | ${this.viewerState.path ? path.relative(this.workspaceRoot, this.viewerState.path) : 'read-only view'}`;
|
|
2560
3128
|
}
|
|
2561
3129
|
if (this.uiMode === 'editor' && this.editorState) {
|
|
2562
3130
|
const dirty = this.editorState.dirty ? 'modified' : 'saved';
|
|
2563
|
-
return `${this.editorState.title} | ${dirty} | Ln ${this.editorState.cursorLine + 1}, Col ${this.editorState.cursorColumn + 1}`;
|
|
3131
|
+
return `${spinner} ${this.editorState.title} | ${dirty} | Ln ${this.editorState.cursorLine + 1}, Col ${this.editorState.cursorColumn + 1}`;
|
|
2564
3132
|
}
|
|
2565
3133
|
const focus = paneDisplayName(this.focusedPane);
|
|
2566
3134
|
if (!this.pendingPrompt && this.focusedPane !== 'sessions') {
|
|
2567
3135
|
const follow = this.paneScroll[this.focusedPane] === 0 ? 'follow' : `scroll ${this.paneScroll[this.focusedPane]}`;
|
|
2568
|
-
return `${this.statusLine} | Focus ${focus} | ${follow}`;
|
|
3136
|
+
return `${spinner} ${this.statusLine} | Focus ${focus} | ${follow}`;
|
|
2569
3137
|
}
|
|
2570
3138
|
if (this.pendingPrompt) {
|
|
2571
3139
|
return this.pendingPrompt.kind === 'clarify'
|
|
2572
|
-
?
|
|
2573
|
-
:
|
|
3140
|
+
? `${spinner} Clarify: ${condenseWhitespace(this.pendingPrompt.prompt)} | Focus ${focus}`
|
|
3141
|
+
: `${spinner} Input: ${condenseWhitespace(this.pendingPrompt.prompt)} | Focus ${focus}`;
|
|
2574
3142
|
}
|
|
2575
|
-
return `${this.statusLine} | Focus ${focus}`;
|
|
3143
|
+
return `${spinner} ${this.statusLine} | Focus ${focus}`;
|
|
2576
3144
|
}
|
|
2577
3145
|
buildInputPrefix() {
|
|
2578
3146
|
if (this.uiMode === 'menu') {
|
|
@@ -2667,6 +3235,12 @@ class FullscreenTui {
|
|
|
2667
3235
|
'Use /cancel to abort the current execution.',
|
|
2668
3236
|
];
|
|
2669
3237
|
}
|
|
3238
|
+
buildTasksPaneBody() {
|
|
3239
|
+
const tracker = this.taskTrackerFor(this.selectedSessionId);
|
|
3240
|
+
const columns = output.columns ?? 120;
|
|
3241
|
+
const widths = computePaneWidths(columns);
|
|
3242
|
+
return tracker.buildPaneBody(widths.right);
|
|
3243
|
+
}
|
|
2670
3244
|
buildContextPaneBody() {
|
|
2671
3245
|
if (!this.sessionDetail) {
|
|
2672
3246
|
return ['No session selected.'];
|
|
@@ -2865,6 +3439,8 @@ class FullscreenTui {
|
|
|
2865
3439
|
['<- ->', 'Pane'],
|
|
2866
3440
|
[this.focusedPane === 'sessions' ? 'Up/Down' : 'PgUp/PgDn', this.focusedPane === 'sessions' ? 'Select' : 'Scroll'],
|
|
2867
3441
|
['End', this.focusedPane === 'sessions' ? 'Bottom' : 'Follow'],
|
|
3442
|
+
['Drag', 'Select'],
|
|
3443
|
+
...(this.busy ? [['Ctrl+T', 'Cancel']] : []),
|
|
2868
3444
|
['/cancel', 'Stop'],
|
|
2869
3445
|
];
|
|
2870
3446
|
}
|
|
@@ -2879,6 +3455,16 @@ class FullscreenTui {
|
|
|
2879
3455
|
? `${title}${this.focusedPane === kind ? ' [focus]' : ''} [follow]`
|
|
2880
3456
|
: `${title}${this.focusedPane === kind ? ' [focus]' : ''} [-${this.paneScroll[kind]}]`;
|
|
2881
3457
|
}
|
|
3458
|
+
cachedCollectTailWrappedLines(cacheKey, body, innerWidth, limit) {
|
|
3459
|
+
const cached = this.wrapCache.get(cacheKey);
|
|
3460
|
+
const bodyKey = `${body.length}:${body.length > 0 ? body[body.length - 1] : ''}:${limit}`;
|
|
3461
|
+
if (cached && cached.bodyRef === body && cached.bodyKey === bodyKey && cached.innerWidth === innerWidth) {
|
|
3462
|
+
return { lines: cached.result, truncated: body.length > 0 && cached.result.length < body.length };
|
|
3463
|
+
}
|
|
3464
|
+
const result = collectTailWrappedLines(body, innerWidth, limit);
|
|
3465
|
+
this.wrapCache.set(cacheKey, { bodyRef: body, bodyKey, innerWidth, result: result.lines });
|
|
3466
|
+
return result;
|
|
3467
|
+
}
|
|
2882
3468
|
getRenderPaneMetrics(kind, width = paneDimensionsForKind(kind, output.columns ?? 120, output.rows ?? 40).width, height = paneDimensionsForKind(kind, output.columns ?? 120, output.rows ?? 40).height) {
|
|
2883
3469
|
if ((kind === 'process' || kind === 'messages') && this.paneScroll[kind] === 0) {
|
|
2884
3470
|
const innerWidth = Math.max(1, width - 2);
|
|
@@ -2886,7 +3472,7 @@ class FullscreenTui {
|
|
|
2886
3472
|
const rawBody = kind === 'messages'
|
|
2887
3473
|
? this.buildMessagesPaneBody({ recentEntryLimit: Math.max(8, viewportLines * 2) })
|
|
2888
3474
|
: this.buildProcessPaneBody({ tailLineLimit: Math.max(16, viewportLines * 4) });
|
|
2889
|
-
const tail =
|
|
3475
|
+
const tail = this.cachedCollectTailWrappedLines(`${kind}:tail`, rawBody, innerWidth, viewportLines);
|
|
2890
3476
|
return {
|
|
2891
3477
|
lines: tail.lines,
|
|
2892
3478
|
start: 0,
|
|
@@ -2904,7 +3490,7 @@ class FullscreenTui {
|
|
|
2904
3490
|
getPaneMetrics(kind, width = paneDimensionsForKind(kind, output.columns ?? 120, output.rows ?? 40).width, height = paneDimensionsForKind(kind, output.columns ?? 120, output.rows ?? 40).height) {
|
|
2905
3491
|
const innerWidth = Math.max(1, width - 2);
|
|
2906
3492
|
const rawBody = this.getPaneBody(kind);
|
|
2907
|
-
const lines =
|
|
3493
|
+
const lines = this.cachedWrapBodyLines(kind, rawBody, innerWidth);
|
|
2908
3494
|
const viewportLines = Math.max(0, height - 2);
|
|
2909
3495
|
const maxStart = Math.max(0, lines.length - viewportLines);
|
|
2910
3496
|
if (kind === 'sessions') {
|
|
@@ -2926,6 +3512,16 @@ class FullscreenTui {
|
|
|
2926
3512
|
maxStartIsExact: true,
|
|
2927
3513
|
};
|
|
2928
3514
|
}
|
|
3515
|
+
cachedWrapBodyLines(cacheKey, body, innerWidth) {
|
|
3516
|
+
const cached = this.wrapCache.get(cacheKey);
|
|
3517
|
+
const bodyKey = `${body.length}:${body.length > 0 ? body[body.length - 1] : ''}`;
|
|
3518
|
+
if (cached && cached.bodyRef === body && cached.bodyKey === bodyKey && cached.innerWidth === innerWidth) {
|
|
3519
|
+
return cached.result;
|
|
3520
|
+
}
|
|
3521
|
+
const result = wrapBodyLines(body, innerWidth);
|
|
3522
|
+
this.wrapCache.set(cacheKey, { bodyRef: body, bodyKey, innerWidth, result });
|
|
3523
|
+
return result;
|
|
3524
|
+
}
|
|
2929
3525
|
getPaneBody(kind) {
|
|
2930
3526
|
switch (kind) {
|
|
2931
3527
|
case 'sessions':
|
|
@@ -2933,7 +3529,9 @@ class FullscreenTui {
|
|
|
2933
3529
|
case 'messages':
|
|
2934
3530
|
return this.buildMessagesPaneBody();
|
|
2935
3531
|
case 'process':
|
|
2936
|
-
return this.buildProcessPaneBody();
|
|
3532
|
+
return this.buildProcessPaneBody({ tailLineLimit: MAX_ACTIVITY_LINES });
|
|
3533
|
+
case 'tasks':
|
|
3534
|
+
return this.buildTasksPaneBody();
|
|
2937
3535
|
case 'context':
|
|
2938
3536
|
return this.buildContextPaneBody();
|
|
2939
3537
|
}
|
|
@@ -2951,8 +3549,7 @@ class FullscreenTui {
|
|
|
2951
3549
|
}
|
|
2952
3550
|
}
|
|
2953
3551
|
formatMessageHeader(entry) {
|
|
2954
|
-
|
|
2955
|
-
return formatDisplayMessageHeader(entry, spinner);
|
|
3552
|
+
return formatDisplayMessageHeader(entry, '');
|
|
2956
3553
|
}
|
|
2957
3554
|
close() {
|
|
2958
3555
|
if (!this.closeResolver) {
|
|
@@ -2962,6 +3559,21 @@ class FullscreenTui {
|
|
|
2962
3559
|
this.closeResolver = null;
|
|
2963
3560
|
resolve();
|
|
2964
3561
|
}
|
|
3562
|
+
manageToolAbortSignal(event) {
|
|
3563
|
+
if (event.type !== 'tool_call' || !event.toolState) {
|
|
3564
|
+
return;
|
|
3565
|
+
}
|
|
3566
|
+
if (event.toolState === 'starting' || event.toolState === 'running') {
|
|
3567
|
+
if (!this.toolAbortController || this.toolAbortController.signal.aborted) {
|
|
3568
|
+
this.toolAbortController = new AbortController();
|
|
3569
|
+
}
|
|
3570
|
+
this.toolAbortSignalRef.signal = this.toolAbortController.signal;
|
|
3571
|
+
}
|
|
3572
|
+
else {
|
|
3573
|
+
this.toolAbortController = null;
|
|
3574
|
+
this.toolAbortSignalRef.signal = undefined;
|
|
3575
|
+
}
|
|
3576
|
+
}
|
|
2965
3577
|
isEditingInput() {
|
|
2966
3578
|
return this.uiMode !== 'editor' && this.inputValue.length > 0;
|
|
2967
3579
|
}
|
|
@@ -3254,6 +3866,7 @@ export async function runFileCommand(workspaceRoot, fileSpec, args, options = {}
|
|
|
3254
3866
|
}, args, options);
|
|
3255
3867
|
}
|
|
3256
3868
|
async function executeRunnableCommand(workspaceRoot, target, args, options = {}) {
|
|
3869
|
+
const usageByModel = {};
|
|
3257
3870
|
const executionLog = options.sessionId
|
|
3258
3871
|
? createSessionExecutionLogWriter({
|
|
3259
3872
|
workspaceRoot,
|
|
@@ -3265,6 +3878,9 @@ async function executeRunnableCommand(workspaceRoot, target, args, options = {})
|
|
|
3265
3878
|
})
|
|
3266
3879
|
: null;
|
|
3267
3880
|
const emitLogEvent = (event) => {
|
|
3881
|
+
if (event.event.type === 'usage') {
|
|
3882
|
+
recordTokenUsage(usageByModel, event.modelId, event.event.usage);
|
|
3883
|
+
}
|
|
3268
3884
|
executionLog?.recordEvent(event);
|
|
3269
3885
|
options.onEvent?.(event);
|
|
3270
3886
|
};
|
|
@@ -3275,12 +3891,14 @@ async function executeRunnableCommand(workspaceRoot, target, args, options = {})
|
|
|
3275
3891
|
stream: true,
|
|
3276
3892
|
sandbox: options.sandbox,
|
|
3277
3893
|
signal: options.signal,
|
|
3894
|
+
toolAbortSignalRef: options.toolAbortSignalRef,
|
|
3278
3895
|
onUserInput: options.onUserInput,
|
|
3279
3896
|
onEvent: (event) => emitLogEvent({ scope: 'child', childSlug: target.childSlug, event }),
|
|
3280
3897
|
onChildEvent: (childSlug, event) => emitLogEvent({ scope: 'child', childSlug, event }),
|
|
3281
3898
|
});
|
|
3282
3899
|
if (options.sessionId) {
|
|
3283
3900
|
await appendConductorSessionMessages(workspaceRoot, options.sessionId, buildPersistedRunnableTranscript(target.inputText, result));
|
|
3901
|
+
await mergeSessionUsage(workspaceRoot, options.sessionId, usageByModel);
|
|
3284
3902
|
}
|
|
3285
3903
|
await executionLog?.finish({
|
|
3286
3904
|
status: result.error ? 'error' : 'success',
|
|
@@ -3296,6 +3914,7 @@ async function executeRunnableCommand(workspaceRoot, target, args, options = {})
|
|
|
3296
3914
|
output: [],
|
|
3297
3915
|
error: error.message,
|
|
3298
3916
|
}));
|
|
3917
|
+
await mergeSessionUsage(workspaceRoot, options.sessionId, usageByModel);
|
|
3299
3918
|
}
|
|
3300
3919
|
await executionLog?.finish({
|
|
3301
3920
|
status: 'error',
|
|
@@ -4019,17 +4638,23 @@ function computePaneWidths(columns) {
|
|
|
4019
4638
|
return { left, center, right };
|
|
4020
4639
|
}
|
|
4021
4640
|
function computeRightPaneHeights(totalHeight) {
|
|
4022
|
-
if (totalHeight <=
|
|
4023
|
-
return { process: Math.max(1, totalHeight -
|
|
4024
|
-
}
|
|
4025
|
-
let process = Math.max(
|
|
4026
|
-
let
|
|
4027
|
-
|
|
4028
|
-
|
|
4029
|
-
|
|
4030
|
-
|
|
4031
|
-
|
|
4032
|
-
|
|
4641
|
+
if (totalHeight <= 12) {
|
|
4642
|
+
return { process: Math.max(1, totalHeight - 2), tasks: 1, context: 1 };
|
|
4643
|
+
}
|
|
4644
|
+
let process = Math.max(6, Math.floor(totalHeight * 0.40));
|
|
4645
|
+
let tasks = Math.max(4, Math.floor(totalHeight * 0.25));
|
|
4646
|
+
let context = totalHeight - process - tasks;
|
|
4647
|
+
if (context < 4) {
|
|
4648
|
+
const deficit = 4 - context;
|
|
4649
|
+
process = Math.max(4, process - deficit);
|
|
4650
|
+
context = totalHeight - process - tasks;
|
|
4651
|
+
}
|
|
4652
|
+
if (context < 4) {
|
|
4653
|
+
const deficit = 4 - context;
|
|
4654
|
+
tasks = Math.max(3, tasks - deficit);
|
|
4655
|
+
context = totalHeight - process - tasks;
|
|
4656
|
+
}
|
|
4657
|
+
return { process, tasks, context };
|
|
4033
4658
|
}
|
|
4034
4659
|
function buildPane(title, body, width, height, kind, active = false) {
|
|
4035
4660
|
if (height <= 0) {
|
|
@@ -4091,6 +4716,37 @@ function expandTabs(text, tabWidth) {
|
|
|
4091
4716
|
export function measureDisplayWidth(text) {
|
|
4092
4717
|
return splitGraphemes(text).reduce((total, grapheme) => total + graphemeDisplayWidth(grapheme), 0);
|
|
4093
4718
|
}
|
|
4719
|
+
function displayColumnToStringIndex(text, displayCol) {
|
|
4720
|
+
let col = 0;
|
|
4721
|
+
let i = 0;
|
|
4722
|
+
const len = text.length;
|
|
4723
|
+
while (i < len) {
|
|
4724
|
+
if (text.charCodeAt(i) === 0x1b && i + 1 < len && text[i + 1] === '[') {
|
|
4725
|
+
const end = text.indexOf('m', i + 2);
|
|
4726
|
+
if (end !== -1) {
|
|
4727
|
+
i = end + 1;
|
|
4728
|
+
continue;
|
|
4729
|
+
}
|
|
4730
|
+
}
|
|
4731
|
+
if (col >= displayCol)
|
|
4732
|
+
return i;
|
|
4733
|
+
const code = text.codePointAt(i);
|
|
4734
|
+
const charWidth = code >= 0x1100 && (code <= 0x115f || code === 0x2329 || code === 0x232a ||
|
|
4735
|
+
(code >= 0x2e80 && code <= 0xa4cf && code !== 0x303f) ||
|
|
4736
|
+
(code >= 0xac00 && code <= 0xd7a3) ||
|
|
4737
|
+
(code >= 0xf900 && code <= 0xfaff) ||
|
|
4738
|
+
(code >= 0xfe10 && code <= 0xfe19) ||
|
|
4739
|
+
(code >= 0xfe30 && code <= 0xfe6f) ||
|
|
4740
|
+
(code >= 0xff01 && code <= 0xff60) ||
|
|
4741
|
+
(code >= 0xffe0 && code <= 0xffe6) ||
|
|
4742
|
+
(code >= 0x1f300 && code <= 0x1f9ff) ||
|
|
4743
|
+
(code >= 0x20000 && code <= 0x2fffd) ||
|
|
4744
|
+
(code >= 0x30000 && code <= 0x3fffd)) ? 2 : 1;
|
|
4745
|
+
col += charWidth;
|
|
4746
|
+
i += code > 0xffff ? 2 : 1;
|
|
4747
|
+
}
|
|
4748
|
+
return i;
|
|
4749
|
+
}
|
|
4094
4750
|
export function padRight(text, width) {
|
|
4095
4751
|
const visibleWidth = measureDisplayWidth(text);
|
|
4096
4752
|
return visibleWidth >= width ? text : `${text}${' '.repeat(width - visibleWidth)}`;
|
|
@@ -4349,8 +5005,20 @@ function renderWindowBody(text, innerWidth, kind) {
|
|
|
4349
5005
|
return `${border}${paint(content, ANSI.yellow, ANSI.bgBlue, ANSI.bold)}${border}`;
|
|
4350
5006
|
}
|
|
4351
5007
|
if (normalized.startsWith('task') || normalized.startsWith('skill')) {
|
|
4352
|
-
return `${border}${paint(content, ANSI.brightWhite, ANSI.bgBlue, ANSI.bold)}
|
|
5008
|
+
return `${border}${paint(content, ANSI.brightWhite, ANSI.bgBlue, ANSI.bold)}`;
|
|
5009
|
+
}
|
|
5010
|
+
}
|
|
5011
|
+
if (kind === 'tasks') {
|
|
5012
|
+
if (text.startsWith('>')) {
|
|
5013
|
+
return `${border}${paint(content, ANSI.brightCyan, ANSI.bgBlue, ANSI.bold)}${border}`;
|
|
5014
|
+
}
|
|
5015
|
+
if (text.startsWith('\u2713')) {
|
|
5016
|
+
return `${border}${paint(content, ANSI.green, ANSI.bgBlue)}${border}`;
|
|
5017
|
+
}
|
|
5018
|
+
if (text.startsWith('\u2717')) {
|
|
5019
|
+
return `${border}${paint(content, ANSI.red, ANSI.bgBlue)}${border}`;
|
|
4353
5020
|
}
|
|
5021
|
+
return `${border}${paint(content, ANSI.white, ANSI.bgBlue)}${border}`;
|
|
4354
5022
|
}
|
|
4355
5023
|
if (normalized === 'task memory' ||
|
|
4356
5024
|
normalized === 'assistant memory' ||
|
|
@@ -4379,6 +5047,8 @@ function paneDisplayName(kind) {
|
|
|
4379
5047
|
return 'Messages';
|
|
4380
5048
|
case 'process':
|
|
4381
5049
|
return 'Execution';
|
|
5050
|
+
case 'tasks':
|
|
5051
|
+
return 'Tasks';
|
|
4382
5052
|
case 'context':
|
|
4383
5053
|
return 'Context';
|
|
4384
5054
|
}
|
|
@@ -4393,6 +5063,8 @@ function paneDimensionsForKind(kind, columns, rows) {
|
|
|
4393
5063
|
return { width: widths.center, height: Math.max(1, rows - 3) };
|
|
4394
5064
|
case 'process':
|
|
4395
5065
|
return { width: widths.right, height: rightHeights.process };
|
|
5066
|
+
case 'tasks':
|
|
5067
|
+
return { width: widths.right, height: rightHeights.tasks };
|
|
4396
5068
|
case 'context':
|
|
4397
5069
|
return { width: widths.right, height: rightHeights.context };
|
|
4398
5070
|
}
|