aegis-bridge 2.12.1 → 2.12.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.
package/dist/session.js CHANGED
@@ -759,9 +759,21 @@ export class SessionManager {
759
759
  const windowHealth = await this.tmux.getWindowHealth(session.windowId);
760
760
  if (!windowHealth.windowExists)
761
761
  return false;
762
- // Pane exit is a direct crash signal when remain-on-exit keeps dead panes visible.
763
- if (windowHealth.paneDead)
762
+ // Pane exit: give CC a grace period to finish after last prompt.
763
+ // CC exits after processing (normal), but paneDead fires BEFORE work is fully recorded.
764
+ // If paneDead just fired and session was recently active, wait before marking dead.
765
+ // This gives CC time to finish writing results before Aegis closes the session.
766
+ if (windowHealth.paneDead && session.status !== 'idle') {
767
+ const msSinceActivity = Date.now() - (session.lastActivity || session.createdAt);
768
+ const GRACE_PERIOD_MS = 5000; // 5 seconds — enough for CC to finish and write results
769
+ if (msSinceActivity < GRACE_PERIOD_MS) {
770
+ // Pane just died right after activity — likely CC is finishing up.
771
+ // Don't mark dead yet — give it 5 seconds to complete.
772
+ return true;
773
+ }
774
+ // Grace period expired — CC has had time to finish. If pane is still dead, it's really dead.
764
775
  return false;
776
+ }
765
777
  // Verify the process inside the pane is still alive
766
778
  const panePid = await this.tmux.listPanePid(session.windowId);
767
779
  if (panePid !== null && !this.tmux.isPidAlive(panePid))
package/dist/tmux.js CHANGED
@@ -7,7 +7,7 @@
7
7
  import { execFile } from 'node:child_process';
8
8
  import { promisify } from 'node:util';
9
9
  import { readdir, rename as fsRename, mkdir, stat } from 'node:fs/promises';
10
- import { existsSync } from 'node:fs';
10
+ import { existsSync, readFileSync } from 'node:fs';
11
11
  import { join } from 'node:path';
12
12
  import { homedir, tmpdir } from 'node:os';
13
13
  import { randomBytes } from 'node:crypto';
@@ -496,7 +496,17 @@ export class TmuxManager {
496
496
  /** Issue #69: Check if a PID is alive using kill -0. */
497
497
  isPidAlive(pid) {
498
498
  try {
499
+ // First check: does process exist?
499
500
  process.kill(pid, 0);
501
+ // Second check: is it a zombie? (zombies still exist but are dead)
502
+ // Read /proc/<pid>/stat synchronously — state is 3rd field
503
+ try {
504
+ const stat = readFileSync(`/proc/${pid}/stat`, 'utf8');
505
+ const match = stat.match(/^\d+ \([^)]+\) ([A-Z])/);
506
+ if (match && match[1] === 'Z')
507
+ return false; // Zombie = dead
508
+ }
509
+ catch { /* /proc check failed — process might have exited */ }
500
510
  return true;
501
511
  }
502
512
  catch { /* ESRCH — process does not exist */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aegis-bridge",
3
- "version": "2.12.1",
3
+ "version": "2.12.3",
4
4
  "type": "module",
5
5
  "description": "Orchestrate Claude Code sessions via API. Create, brief, monitor, refine, ship.",
6
6
  "main": "dist/server.js",