orchestrix-yuri 2.6.0 → 2.7.0
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.
|
@@ -347,10 +347,9 @@ async function createSession(engineConfig) {
|
|
|
347
347
|
// Ensure CLAUDE.md has channel mode instructions (survives compact)
|
|
348
348
|
ensureClaudeMd(projectRoot);
|
|
349
349
|
|
|
350
|
-
//
|
|
351
|
-
// so the user can `tmux attach -t yuri-gateway` to debug
|
|
350
|
+
// Kill existing broken session (ensureSession already checked if it was healthy)
|
|
352
351
|
if (hasSession(sessionName)) {
|
|
353
|
-
log.tmux(`
|
|
352
|
+
log.tmux(`Replacing unhealthy session "${sessionName}"`);
|
|
354
353
|
tmuxSafe(`kill-session -t ${sessionName}`);
|
|
355
354
|
}
|
|
356
355
|
|
|
@@ -446,7 +445,7 @@ function injectMessage(name, text) {
|
|
|
446
445
|
* We also track whether content has changed since injection (via marker)
|
|
447
446
|
* to avoid returning before Claude has even started responding.
|
|
448
447
|
*/
|
|
449
|
-
async function captureResponse(name,
|
|
448
|
+
async function captureResponse(name, engineConfig) {
|
|
450
449
|
const timeout = engineConfig.timeout || 300000;
|
|
451
450
|
const pollInterval = engineConfig.poll_interval || 2000;
|
|
452
451
|
const stableThreshold = engineConfig.stable_count || 3;
|
|
@@ -467,7 +466,7 @@ async function captureResponse(name, marker, engineConfig) {
|
|
|
467
466
|
if (Date.now() > deadline) {
|
|
468
467
|
log.warn('Response capture timed out');
|
|
469
468
|
const raw = capturePaneRaw(name, 500);
|
|
470
|
-
return resolve(extractResponse(raw,
|
|
469
|
+
return resolve(extractResponse(raw, baselineRaw));
|
|
471
470
|
}
|
|
472
471
|
|
|
473
472
|
// Session died
|
|
@@ -484,13 +483,11 @@ async function captureResponse(name, marker, engineConfig) {
|
|
|
484
483
|
}
|
|
485
484
|
|
|
486
485
|
// P1: Completion message — most reliable done signal
|
|
487
|
-
// Only check after content has changed (Claude has started responding)
|
|
488
486
|
if (contentChanged && hasCompletionMessage(paneTail(name, 15))) {
|
|
489
|
-
return resolve(extractResponse(raw,
|
|
487
|
+
return resolve(extractResponse(raw, baselineRaw));
|
|
490
488
|
}
|
|
491
489
|
|
|
492
490
|
// P2: Content stability — pane unchanged for N polls
|
|
493
|
-
// Only trigger after content has changed from baseline
|
|
494
491
|
if (hash === lastHash) {
|
|
495
492
|
stableCount++;
|
|
496
493
|
} else {
|
|
@@ -500,7 +497,7 @@ async function captureResponse(name, marker, engineConfig) {
|
|
|
500
497
|
|
|
501
498
|
if (contentChanged && stableCount >= stableThreshold) {
|
|
502
499
|
log.tmux('Response detected via content stability');
|
|
503
|
-
return resolve(extractResponse(raw,
|
|
500
|
+
return resolve(extractResponse(raw, baselineRaw));
|
|
504
501
|
}
|
|
505
502
|
|
|
506
503
|
setTimeout(poll, pollInterval);
|
|
@@ -512,49 +509,60 @@ async function captureResponse(name, marker, engineConfig) {
|
|
|
512
509
|
}
|
|
513
510
|
|
|
514
511
|
/**
|
|
515
|
-
* Extract the assistant's response
|
|
516
|
-
*
|
|
512
|
+
* Extract the assistant's response by diffing current pane against baseline.
|
|
513
|
+
*
|
|
514
|
+
* Strategy: the baseline was captured right after message injection (before
|
|
515
|
+
* Claude started responding). The current capture has Claude's response + TUI
|
|
516
|
+
* chrome. We diff the two to find only the new content.
|
|
517
|
+
*
|
|
518
|
+
* @param {string} currentRaw - Current pane capture
|
|
519
|
+
* @param {string} baselineRaw - Pane capture from right after injection
|
|
520
|
+
* @returns {{reply: string, raw: string}}
|
|
517
521
|
*/
|
|
518
|
-
function extractResponse(
|
|
519
|
-
const
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
// Find the last occurrence of the marker (in case of scrollback)
|
|
523
|
-
for (let i = lines.length - 1; i >= 0; i--) {
|
|
524
|
-
if (lines[i].includes(marker)) {
|
|
525
|
-
markerIdx = i;
|
|
526
|
-
break;
|
|
527
|
-
}
|
|
528
|
-
}
|
|
522
|
+
function extractResponse(currentRaw, baselineRaw) {
|
|
523
|
+
const currentLines = currentRaw.split('\n');
|
|
524
|
+
const baselineLines = new Set(baselineRaw.split('\n'));
|
|
529
525
|
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
const afterMarker = lines.slice(markerIdx + 1).join('\n');
|
|
534
|
-
responseText = stripChrome(afterMarker);
|
|
535
|
-
} else {
|
|
536
|
-
log.warn(`Marker not found in pane output, using last 50 lines as fallback`);
|
|
537
|
-
const tail = lines.slice(-50).join('\n');
|
|
538
|
-
responseText = stripChrome(tail);
|
|
539
|
-
}
|
|
526
|
+
// Find lines that are new (not in baseline)
|
|
527
|
+
const newLines = currentLines.filter((line) => !baselineLines.has(line));
|
|
528
|
+
let responseText = stripChrome(newLines.join('\n'));
|
|
540
529
|
|
|
541
|
-
//
|
|
530
|
+
// Remove trailing indicators and collapse blank lines
|
|
542
531
|
responseText = responseText
|
|
543
532
|
.replace(/[○●◐◑]\s*$/g, '')
|
|
544
533
|
.replace(/\n{3,}/g, '\n\n')
|
|
545
534
|
.trim();
|
|
546
535
|
|
|
547
|
-
|
|
536
|
+
if (!responseText) {
|
|
537
|
+
log.warn('No new content found after diffing pane output');
|
|
538
|
+
// Fallback: strip the whole current output
|
|
539
|
+
responseText = stripChrome(currentLines.slice(-30).join('\n')).trim();
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
return { reply: responseText || '(no response captured)', raw: currentRaw };
|
|
548
543
|
}
|
|
549
544
|
|
|
550
545
|
// ── Public API ─────────────────────────────────────────────────────────────────
|
|
551
546
|
|
|
552
547
|
/**
|
|
553
548
|
* Ensure the tmux session is alive and ready.
|
|
554
|
-
*
|
|
549
|
+
* - If session already exists with Claude Code running → reuse it
|
|
550
|
+
* - If session doesn't exist → create fresh
|
|
551
|
+
* - If session exists but Claude Code crashed → recreate
|
|
555
552
|
*/
|
|
556
553
|
async function ensureSession(engineConfig) {
|
|
557
|
-
|
|
554
|
+
const sessionName = engineConfig.tmux_session || DEFAULT_SESSION;
|
|
555
|
+
|
|
556
|
+
// Fast path: session alive and marked ready in this process
|
|
557
|
+
if (_sessionName === sessionName && hasSession(sessionName) && _sessionReady) {
|
|
558
|
+
return;
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
// Check if session exists from a previous gateway run
|
|
562
|
+
if (hasSession(sessionName) && isStarted(sessionName)) {
|
|
563
|
+
log.tmux(`Reusing existing session "${sessionName}" (Claude Code is running)`);
|
|
564
|
+
_sessionName = sessionName;
|
|
565
|
+
_sessionReady = true;
|
|
558
566
|
return;
|
|
559
567
|
}
|
|
560
568
|
|
|
@@ -573,12 +581,10 @@ async function ensureSession(engineConfig) {
|
|
|
573
581
|
log.warn(`Session init attempt ${attempt}/${maxRetries} failed: ${err.message}`);
|
|
574
582
|
if (attempt === maxRetries) {
|
|
575
583
|
log.error('All init attempts failed. Check Claude Code installation and tmux.');
|
|
576
|
-
log.info(`Debug: tmux attach -t ${
|
|
584
|
+
log.info(`Debug: tmux attach -t ${sessionName}`);
|
|
577
585
|
throw err;
|
|
578
586
|
}
|
|
579
|
-
|
|
580
|
-
const sn = engineConfig.tmux_session || DEFAULT_SESSION;
|
|
581
|
-
if (hasSession(sn)) tmuxSafe(`kill-session -t ${sn}`);
|
|
587
|
+
if (hasSession(sessionName)) tmuxSafe(`kill-session -t ${sessionName}`);
|
|
582
588
|
await new Promise((r) => setTimeout(r, 3000));
|
|
583
589
|
}
|
|
584
590
|
}
|
|
@@ -617,13 +623,9 @@ async function callClaude(opts) {
|
|
|
617
623
|
await proactiveCompact(_sessionName);
|
|
618
624
|
}
|
|
619
625
|
|
|
620
|
-
//
|
|
621
|
-
|
|
622
|
-
const
|
|
623
|
-
|
|
624
|
-
// Inject and capture
|
|
625
|
-
injectMessage(_sessionName, markedPrompt);
|
|
626
|
-
const result = await captureResponse(_sessionName, marker, config);
|
|
626
|
+
// Inject user message (no marker — extraction uses completion message position)
|
|
627
|
+
injectMessage(_sessionName, prompt);
|
|
628
|
+
const result = await captureResponse(_sessionName, config);
|
|
627
629
|
|
|
628
630
|
_messageCount++;
|
|
629
631
|
resolve(result);
|