Skip to content

[Bugfix #584] Pace multi-line afx send messages to prevent paste detection#657

Merged
waleedkadous merged 4 commits intomainfrom
builder/bugfix-584-af-send-multi-line-messages-3-
Apr 5, 2026
Merged

[Bugfix #584] Pace multi-line afx send messages to prevent paste detection#657
waleedkadous merged 4 commits intomainfrom
builder/bugfix-584-af-send-multi-line-messages-3-

Conversation

@waleedkadous
Copy link
Copy Markdown
Contributor

@waleedkadous waleedkadous commented Apr 5, 2026

Summary

Fixes #584

Root Cause

When afx send delivers a message longer than 3 lines, the entire formatted text is written to the PTY in a single session.write() call. The receiving terminal (Claude Code) classifies the rapid multi-line input as a "paste" operation, which causes the final \r (Enter) — sent 50ms later — to be swallowed. The message appears on screen but never gets submitted.

Fix

Extracted a writeMessageToSession() helper in message-write.ts that paces multi-line output:

  • ≤3 lines: Single write() + 50ms delayed Enter (existing behavior, works fine)
  • >3 lines: Line-by-line writes with 10ms gaps between lines, then Enter after an 80ms gap from the last line

All three delivery paths now use this helper:

  1. Immediate delivery in handleSend() (tower-routes.ts)
  2. Deferred delivery via deliverBufferedMessage() (tower-routes.ts)
  3. Cron message delivery in tower-cron.ts

The helper also supports a delayOffset parameter and returns the end time, enabling SendBuffer.flush() to serialize multiple buffered messages without interleaving.

Test Plan

  • Added 9 regression tests covering short/long messages, noEnter, delayOffset, and multi-message serialization
  • Existing send-buffer tests pass (13/13)
  • Full test suite passes
  • TypeScript compiles cleanly

CMAP Review

  • Claude: APPROVE — clean implementation, correct timing math, good test coverage
  • Codex: REQUEST_CHANGES (addressed) — identified interleaving risk when multiple buffered messages flush to the same session. Fixed by adding delayOffset parameter and return value to serialize writes.
  • Gemini: REQUEST_CHANGES (addressed) — identified same vulnerable pattern in tower-cron.ts and suggested extracting to shared module. Fixed by creating message-write.ts and updating tower-cron.ts.

…tection

When afx send delivers messages >3 lines, write line-by-line with 10ms
delays instead of a single write() call. This prevents the receiving
terminal from classifying the input as a paste and swallowing the
final Enter.
When multiple multi-line messages are buffered for the same session,
writeMessageToSession now accepts a delayOffset and returns the end
time so SendBuffer.flush() can chain messages without interleaving.
Move pacing logic to message-write.ts to avoid circular dependency
between tower-routes.ts and tower-cron.ts. Also fix tower-cron.ts
cron message delivery to use the same pacing.
@waleedkadous
Copy link
Copy Markdown
Contributor Author

Architect Review

Clean fix. Good extraction of writeMessageToSession into a shared module — consolidates three separate write sites into one. The line-by-line pacing for >3 lines with serialized offsets to prevent interleaving is well thought through.

Go ahead and merge.

@waleedkadous waleedkadous merged commit b40357d into main Apr 5, 2026
6 checks passed
@waleedkadous waleedkadous deleted the builder/bugfix-584-af-send-multi-line-messages-3- branch April 5, 2026 06:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

af send: multi-line messages (>3 lines) treated as paste, final Enter swallowed

1 participant