orchestrix-yuri 2.3.3 → 2.4.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.
package/lib/gateway/config.js
CHANGED
|
@@ -20,7 +20,7 @@ const DEFAULTS = {
|
|
|
20
20
|
engine: {
|
|
21
21
|
skill: 'yuri',
|
|
22
22
|
tmux_session: 'yuri-gateway',
|
|
23
|
-
startup_timeout:
|
|
23
|
+
startup_timeout: 60000, // ms to wait for Claude Code to initialize
|
|
24
24
|
poll_interval: 2000, // ms between capture-pane polls
|
|
25
25
|
stable_count: 3, // consecutive stable polls before declaring done
|
|
26
26
|
max_retries: 3, // session restart retries before error
|
|
@@ -143,24 +143,28 @@ function tmuxSafe(cmd) {
|
|
|
143
143
|
|
|
144
144
|
// ── Claude Code TUI Indicators ─────────────────────────────────────────────────
|
|
145
145
|
//
|
|
146
|
-
// Claude Code
|
|
146
|
+
// Claude Code TUI state indicators vary by version and statusline config:
|
|
147
147
|
//
|
|
148
|
-
//
|
|
149
|
-
//
|
|
150
|
-
//
|
|
148
|
+
// Idle indicators (any of these = ready for input):
|
|
149
|
+
// ○ (U+25CB) — circle idle indicator (shown with certain statusline configs)
|
|
150
|
+
// ❯ — prompt cursor (always shown when idle, most reliable)
|
|
151
151
|
//
|
|
152
|
-
//
|
|
153
|
-
//
|
|
152
|
+
// Processing indicators:
|
|
153
|
+
// ● (U+25CF) — filled circle, active generation
|
|
154
|
+
// Braille spinner: ⠋ ⠙ ⠹ ⠸ ⠼ ⠴ ⠦ ⠧ ⠇ ⠏
|
|
154
155
|
//
|
|
155
|
-
//
|
|
156
|
-
//
|
|
157
|
-
//
|
|
156
|
+
// Status line elements (NOT state indicators):
|
|
157
|
+
// ◐ — effort level indicator (e.g. "◐ medium · /effort"), NOT approval prompt
|
|
158
|
+
//
|
|
159
|
+
// Completion message (past-tense verb + duration):
|
|
160
|
+
// "Baked for 31s", "Worked for 2m 45s"
|
|
161
|
+
// Pattern: /[A-Z][a-z]*ed for \d+/
|
|
158
162
|
//
|
|
159
163
|
// ────────────────────────────────────────────────────────────────────────────────
|
|
160
164
|
|
|
161
165
|
const BRAILLE_SPINNER = /[⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏]/;
|
|
162
166
|
const COMPLETION_RE = /[A-Z][a-z]*ed for \d+/;
|
|
163
|
-
const IDLE_RE =
|
|
167
|
+
const IDLE_RE = /[○❯]/;
|
|
164
168
|
const PROCESSING_RE = /●/;
|
|
165
169
|
|
|
166
170
|
/**
|
|
@@ -202,10 +206,6 @@ function paneTail(name, n) {
|
|
|
202
206
|
/**
|
|
203
207
|
* Detect Claude Code's current state from pane output.
|
|
204
208
|
*
|
|
205
|
-
* Note: We launch with --dangerously-skip-permissions, so approval prompts (◐)
|
|
206
|
-
* should never appear. The detection is kept for robustness but no auto-approve
|
|
207
|
-
* action is taken — sending blind 'y' keystrokes is dangerous.
|
|
208
|
-
*
|
|
209
209
|
* @returns {'idle'|'processing'|'complete'|'unknown'}
|
|
210
210
|
*/
|
|
211
211
|
function detectState(name) {
|
|
@@ -325,32 +325,43 @@ async function createSession(engineConfig) {
|
|
|
325
325
|
|
|
326
326
|
const binary = getClaudeBinary();
|
|
327
327
|
const projectRoot = resolveProjectRoot() || os.homedir();
|
|
328
|
+
log.tmux(`Binary: ${binary}`);
|
|
329
|
+
log.tmux(`Project root: ${projectRoot}`);
|
|
328
330
|
|
|
329
331
|
// Ensure CLAUDE.md has channel mode instructions (survives compact)
|
|
330
332
|
ensureClaudeMd(projectRoot);
|
|
331
333
|
|
|
332
|
-
//
|
|
334
|
+
// Only kill existing session if it exists — don't kill on retry
|
|
335
|
+
// so the user can `tmux attach -t yuri-gateway` to debug
|
|
333
336
|
if (hasSession(sessionName)) {
|
|
337
|
+
log.tmux(`Killing existing session "${sessionName}"`);
|
|
334
338
|
tmuxSafe(`kill-session -t ${sessionName}`);
|
|
335
339
|
}
|
|
336
340
|
|
|
337
341
|
// Create session with generous scrollback
|
|
338
342
|
tmux(`new-session -d -s ${sessionName} -n claude -c "${projectRoot}"`);
|
|
339
343
|
tmux(`set-option -t ${sessionName} history-limit ${HISTORY_LIMIT}`);
|
|
344
|
+
log.tmux(`Session "${sessionName}" created, launching Claude Code...`);
|
|
340
345
|
|
|
341
346
|
// Set auto-compact threshold to 80% (default is 95%)
|
|
342
|
-
// This gives comfortable buffer before context pressure
|
|
343
347
|
const compactPct = engineConfig.autocompact_pct || 80;
|
|
344
348
|
tmux(`send-keys -t ${sessionName}:0 'export CLAUDE_AUTOCOMPACT_PCT_OVERRIDE=${compactPct}' Enter`);
|
|
345
349
|
|
|
346
350
|
// Launch Claude Code in interactive mode
|
|
347
351
|
tmux(`send-keys -t ${sessionName}:0 '"${binary}" --dangerously-skip-permissions' Enter`);
|
|
348
352
|
|
|
349
|
-
// Wait for Claude Code to initialize (detect idle indicator)
|
|
350
|
-
|
|
353
|
+
// Wait for Claude Code to initialize (detect idle indicator ○)
|
|
354
|
+
// Default 60s — Claude Code needs time to load CLAUDE.md, connect MCP servers, etc.
|
|
355
|
+
const startupTimeout = engineConfig.startup_timeout || 60000;
|
|
356
|
+
log.tmux(`Waiting for Claude Code to become idle (timeout: ${startupTimeout / 1000}s)...`);
|
|
351
357
|
const started = await waitForIdle(sessionName, startupTimeout);
|
|
352
358
|
if (!started) {
|
|
353
|
-
|
|
359
|
+
// Don't kill session on failure — let user debug with tmux attach
|
|
360
|
+
const tail = paneTail(sessionName, 10);
|
|
361
|
+
log.error(`Claude Code did not become idle within ${startupTimeout / 1000}s`);
|
|
362
|
+
log.error(`Last pane output:\n${tail}`);
|
|
363
|
+
log.info(`Debug: tmux attach -t ${sessionName}`);
|
|
364
|
+
throw new Error(`Claude Code did not become idle within ${startupTimeout / 1000}s`);
|
|
354
365
|
}
|
|
355
366
|
|
|
356
367
|
// Send L1 context as the initial system message.
|
|
@@ -358,6 +369,7 @@ async function createSession(engineConfig) {
|
|
|
358
369
|
// so we only inject L1 global memory here to prime the session.
|
|
359
370
|
const l1 = loadL1Context();
|
|
360
371
|
if (l1) {
|
|
372
|
+
log.tmux('Injecting L1 context...');
|
|
361
373
|
await injectMessage(sessionName, l1);
|
|
362
374
|
await waitForIdle(sessionName, 120000); // allow up to 2min for L1 processing
|
|
363
375
|
}
|
|
@@ -545,8 +557,14 @@ async function ensureSession(engineConfig) {
|
|
|
545
557
|
return;
|
|
546
558
|
} catch (err) {
|
|
547
559
|
log.warn(`Session init attempt ${attempt}/${maxRetries} failed: ${err.message}`);
|
|
548
|
-
if (attempt === maxRetries)
|
|
549
|
-
|
|
560
|
+
if (attempt === maxRetries) {
|
|
561
|
+
log.error('All init attempts failed. Check Claude Code installation and tmux.');
|
|
562
|
+
log.info(`Debug: tmux attach -t ${engineConfig.tmux_session || DEFAULT_SESSION}`);
|
|
563
|
+
throw err;
|
|
564
|
+
}
|
|
565
|
+
// Kill session before retry so createSession starts fresh
|
|
566
|
+
const sn = engineConfig.tmux_session || DEFAULT_SESSION;
|
|
567
|
+
if (hasSession(sn)) tmuxSafe(`kill-session -t ${sn}`);
|
|
550
568
|
await new Promise((r) => setTimeout(r, 3000));
|
|
551
569
|
}
|
|
552
570
|
}
|