orchestrix-yuri 2.3.1 → 2.3.3

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.
@@ -83,16 +83,11 @@ class TelegramAdapter {
83
83
  });
84
84
 
85
85
  // Force-disconnect any stale polling connection before starting.
86
- // deleteWebhook clears webhooks but does NOT terminate existing
87
- // long-polling getUpdates connections. A short getUpdates call
88
- // with timeout=0 "steals" the connection, terminating the old one.
86
+ // deleteWebhook only clears webhooks, NOT existing long-polling connections.
87
+ // A direct getUpdates call with timeout=0 "steals" the polling slot,
88
+ // terminating any other instance's connection.
89
89
  log.telegram('Connecting...');
90
- try {
91
- await this.bot.api.deleteWebhook({ drop_pending_updates: true });
92
- await this.bot.api.raw.getUpdates({ offset: -1, limit: 1, timeout: 0 });
93
- } catch {
94
- // ignore — best effort cleanup
95
- }
90
+ await forceDisconnectPolling(this.token);
96
91
 
97
92
  // Start polling
98
93
  await this.bot.start({
@@ -144,4 +139,38 @@ function splitMessage(text, maxLength) {
144
139
  return chunks;
145
140
  }
146
141
 
142
+ /**
143
+ * Force-disconnect any stale polling session via direct Telegram API calls.
144
+ * Uses native https to avoid grammy API quirks.
145
+ */
146
+ function forceDisconnectPolling(token) {
147
+ const https = require('https');
148
+
149
+ const call = (method, body) => new Promise((resolve) => {
150
+ const data = JSON.stringify(body);
151
+ const req = https.request({
152
+ hostname: 'api.telegram.org',
153
+ path: `/bot${token}/${method}`,
154
+ method: 'POST',
155
+ headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(data) },
156
+ timeout: 10000,
157
+ }, (res) => {
158
+ let buf = '';
159
+ res.on('data', (d) => { buf += d; });
160
+ res.on('end', () => resolve(buf));
161
+ });
162
+ req.on('error', () => resolve(null));
163
+ req.on('timeout', () => { req.destroy(); resolve(null); });
164
+ req.write(data);
165
+ req.end();
166
+ });
167
+
168
+ return (async () => {
169
+ await call('deleteWebhook', { drop_pending_updates: true });
170
+ await call('getUpdates', { offset: -1, limit: 1, timeout: 0 });
171
+ // Brief pause to let Telegram release the polling slot
172
+ await new Promise((r) => setTimeout(r, 1000));
173
+ })();
174
+ }
175
+
147
176
  module.exports = { TelegramAdapter };
@@ -162,7 +162,6 @@ const BRAILLE_SPINNER = /[⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏]/;
162
162
  const COMPLETION_RE = /[A-Z][a-z]*ed for \d+/;
163
163
  const IDLE_RE = /○/;
164
164
  const PROCESSING_RE = /●/;
165
- const APPROVAL_RE = /◐/;
166
165
 
167
166
  /**
168
167
  * Strip TUI chrome from captured pane output.
@@ -203,7 +202,11 @@ function paneTail(name, n) {
203
202
  /**
204
203
  * Detect Claude Code's current state from pane output.
205
204
  *
206
- * @returns {'idle'|'processing'|'approval'|'complete'|'unknown'}
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
+ * @returns {'idle'|'processing'|'complete'|'unknown'}
207
210
  */
208
211
  function detectState(name) {
209
212
  const tail = paneTail(name, 15);
@@ -214,17 +217,12 @@ function detectState(name) {
214
217
  return 'complete';
215
218
  }
216
219
 
217
- // Priority 2: Approval promptneeds immediate response
218
- if (APPROVAL_RE.test(tail)) {
219
- return 'approval';
220
- }
221
-
222
- // Priority 3: Idle indicator — waiting for input
220
+ // Priority 2: Idle indicatorwaiting for input
223
221
  if (IDLE_RE.test(tail)) {
224
222
  return 'idle';
225
223
  }
226
224
 
227
- // Priority 4: Processing indicator — still working
225
+ // Priority 3: Processing indicator — still working
228
226
  if (PROCESSING_RE.test(tail) || BRAILLE_SPINNER.test(tail)) {
229
227
  return 'processing';
230
228
  }
@@ -241,20 +239,6 @@ function isIdle(name) {
241
239
  return state === 'idle' || state === 'complete';
242
240
  }
243
241
 
244
- /**
245
- * Detect if Claude Code is showing an approval prompt (◐).
246
- */
247
- function isApprovalPrompt(name) {
248
- return detectState(name) === 'approval';
249
- }
250
-
251
- /**
252
- * Detect if Claude Code is actively processing (● or spinner).
253
- */
254
- function isProcessing(name) {
255
- return detectState(name) === 'processing';
256
- }
257
-
258
242
  // ── Context Management ─────────────────────────────────────────────────────────
259
243
  //
260
244
  // Claude Code has built-in auto-compact that triggers at ~95% context capacity.
@@ -398,11 +382,6 @@ function waitForIdle(name, timeoutMs) {
398
382
  return resolve(false);
399
383
  }
400
384
 
401
- // Auto-approve any permission prompts
402
- if (isApprovalPrompt(name)) {
403
- tmuxSafe(`send-keys -t ${name}:0 'y' Enter`);
404
- }
405
-
406
385
  if (isIdle(name)) {
407
386
  return resolve(true);
408
387
  }
@@ -431,11 +410,10 @@ function injectMessage(name, text) {
431
410
  /**
432
411
  * Capture the response after injecting a message.
433
412
  *
434
- * Detection priority (mirrors monitor-agent.sh):
413
+ * Detection priority:
435
414
  * P1: Completion message — "[Verb]ed for [N]s/m" (e.g. "Baked for 31s")
436
415
  * P2: Idle indicator — ○ appears in pane tail
437
- * P3: Approval prompt detected, auto-approve with 'y'
438
- * P4: Content stability — 3 consecutive polls with identical MD5 hash
416
+ * P3: Content stability3 consecutive polls with identical MD5 hash
439
417
  */
440
418
  async function captureResponse(name, marker, engineConfig) {
441
419
  const timeout = engineConfig.timeout || 300000;
@@ -475,16 +453,6 @@ async function captureResponse(name, marker, engineConfig) {
475
453
  return setTimeout(poll, pollInterval);
476
454
  }
477
455
 
478
- // P3: Auto-approve permission prompts (◐)
479
- if (state === 'approval') {
480
- tmuxSafe(`send-keys -t ${name}:0 'y' Enter`);
481
- sawProcessing = true; // approval implies processing started
482
- stableCount = 0;
483
- lastHash = hash;
484
- // Brief pause after approval before next poll
485
- return setTimeout(poll, 2000);
486
- }
487
-
488
456
  // P1: Completion message — most reliable done signal
489
457
  if (state === 'complete' && sawProcessing) {
490
458
  return resolve(extractResponse(raw, marker));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "orchestrix-yuri",
3
- "version": "2.3.1",
3
+ "version": "2.3.3",
4
4
  "description": "Yuri — Meta-Orchestrator for Orchestrix. Drive your entire project lifecycle with natural language.",
5
5
  "main": "lib/installer.js",
6
6
  "bin": {