Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
232 changes: 232 additions & 0 deletions docs/known-issues/2026-07-16-codex-app-false-busy.md

Large diffs are not rendered by default.

32 changes: 21 additions & 11 deletions src/adapters/backend/herdr-backend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ export class HerdrBackend implements SessionBackend {
private lastText = '';
private exited = false;
private started = false;
private actuallyReattached = false;
private cols = 200;
private rows = 50;
private agentProbeFailures = 0;
Expand Down Expand Up @@ -212,7 +213,7 @@ export class HerdrBackend implements SessionBackend {
}

get isReattach(): boolean {
return this.opts.isReattach ?? false;
return this.actuallyReattached;
}

spawn(bin: string, args: string[], opts: SpawnOpts): void {
Expand Down Expand Up @@ -245,6 +246,7 @@ export class HerdrBackend implements SessionBackend {

const external = this.opts.externalTarget;
if (external) {
this.actuallyReattached = false;
this.paneId = external.paneId ?? external.target;
} else {
// Reuse an existing `botmux` agent ONLY when we're genuinely re-attaching
Expand All @@ -255,10 +257,12 @@ export class HerdrBackend implements SessionBackend {
// row from persisted metadata, and reuse would skip `agent start` so the
// new command never ran. killSession() now deletes that metadata, but we
// also gate reuse on isReattach so a stale row can never be adopted.
const existing = this.isReattach ? this.getAgent() : undefined;
const existing = this.opts.isReattach ? this.getAgent() : undefined;
if (existing) {
this.actuallyReattached = true;
this.paneId = existing.pane_id;
} else {
this.actuallyReattached = false;
const started = jsonCommand(herdrSessionArgs(this.sessionName, [
'agent', 'start', this.agentName,
'--cwd', opts.cwd,
Expand All @@ -278,25 +282,31 @@ export class HerdrBackend implements SessionBackend {
// - Re-attach / external adopt: snapshot the current screen so we only
// stream new deltas. Worker.ts explicitly seeds the initial screen
// via captureCurrentScreen() in those paths.
this.lastText = (this.isReattach || this.opts.externalTarget) ? this.readRecentAnsi() : '';
this.lastText = (this.actuallyReattached || this.opts.externalTarget) ? this.readRecentAnsi() : '';
this.startPolling();
this.startStatusWatcher();
}

write(data: string): void {
if (this.exited) return;
write(data: string): boolean {
if (this.exited) return false;
const target = this.paneId ?? this.agentName;
runHerdr(herdrSessionArgs(this.sessionName, ['pane', 'send-text', target, data]), { timeout: 5000 });
return runHerdr(
herdrSessionArgs(this.sessionName, ['pane', 'send-text', target, data]),
{ timeout: 5000 },
);
}

sendText(text: string): void {
this.write(text);
sendText(text: string): boolean {
return this.write(text);
}

sendSpecialKeys(...keys: string[]): void {
if (this.exited) return;
sendSpecialKeys(...keys: string[]): boolean {
if (this.exited) return false;
const target = this.paneId ?? this.agentName;
runHerdr(herdrSessionArgs(this.sessionName, ['pane', 'send-keys', target, ...keys]), { timeout: 5000 });
return runHerdr(
herdrSessionArgs(this.sessionName, ['pane', 'send-keys', target, ...keys]),
{ timeout: 5000 },
);
}

pasteText(text: string): void {
Expand Down
6 changes: 4 additions & 2 deletions src/adapters/backend/pty-backend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,10 @@ export class PtyBackend implements SessionBackend {
logger.debug(`[pty] spawned pid=${this.process.pid}`);
}

write(data: string): void {
this.process?.write(data);
write(data: string): boolean {
if (!this.process) return false;
this.process.write(data);
return true;
}

resize(cols: number, rows: number): void {
Expand Down
Loading