#20798: fix(process): Add delayMs parameter to send-keys for TUI compatibility
channel: discord
agents
size: S
Cluster:
Model Reasoning Fixes
## 🔧 Fix
Resolves #20797
### Problem
When automating TUI (Terminal UI) applications through OpenClaw's `process` tool, sending multiple keys at once causes a race condition where keys are lost or ignored. This happens because all keys are sent to the PTY immediately, before the TUI has time to process the first key and update its display state.
**Real-world impact**: Automating Claude Code's `/slack-agent` skill fails - menu navigation keys (`Down`, `Down`, `Return`) don't work as expected, and direct text input ("1_tech") is ignored in favor of cached values ("5_chitchat").
### Solution
Added an optional `delayMs` parameter to the `send-keys` action in `src/agents/bash-tools.process.ts`:
- **When `delayMs` is provided**: Keys are sent individually with `sleep(delayMs)` between them (except after the last key)
- **When `delayMs` is omitted**: Behavior unchanged - all keys sent at once (backward compatible)
### Changes
- `src/agents/bash-tools.process.ts`:
- Added `delayMs` to Zod schema (lines 56-67) with `minimum: 0` constraint
- Updated TypeScript type definition (line 180)
- Implemented sequential key sending with delays (lines 486-532)
- Preserved backward compatibility
### Usage Example
**Before (unreliable with TUI apps):**
```typescript
process({
action: 'send-keys',
sessionId: 'xxx',
keys: ['Down', 'Down', 'Return']
// All keys sent at once → race condition!
});
```
**After (reliable):**
```typescript
process({
action: 'send-keys',
sessionId: 'xxx',
keys: ['Down', 'Down', 'Return'],
delayMs: 200 // 200ms between each key
// Keys sent sequentially with delays → TUI can keep up!
});
```
### Testing
- ✅ Backward compatibility: Existing code without `delayMs` works unchanged
- ✅ Build successful: `pnpm build` passes
- ✅ Manual testing: Verified with Claude Code's interactive menus
### Notes
- **AI-assisted**: This fix was implemented with Claude Code
- **Testing**: Manually tested with Claude Code TUI; automated tests would require mocking PTY interactions
- **Alternative approaches considered**:
- Using `paste` for text input (works but doesn't solve menu navigation)
- Adding `wait-for-output` action (more complex, deferred)
- Making `delayMs` the default (breaking change)
### Commit
- `bb9a32100`: fix(process): add delayMs parameter to send-keys for TUI compatibility
---
**Reviewer notes:**
- Focus on the sequential sending logic (lines 486-532)
- Verify the delay is NOT applied after the last key (performance optimization)
- Confirm Zod schema matches TypeScript types
<!-- greptile_comment -->
<h3>Greptile Summary</h3>
Adds optional `delayMs` parameter to the `send-keys` action in the process tool, enabling sequential key sending with configurable delays between keys. This solves race conditions when automating TUI applications that need time to process each keystroke before receiving the next one.
- Zod schema updated with `delayMs` field (minimum: 0) on lines 61-68
- TypeScript type definition updated on line 180
- Sequential sending logic implemented on lines 486-532
- Backward compatible: omitting `delayMs` preserves original behavior (all keys sent at once)
- Delay intentionally skipped after last key for performance
**Potential issue**: When `delayMs` is used, `hex` and `literal` parameters are only included with the first key (line 500-501). The non-delayed path sends all parameters in a single `encodeKeySequence` call. Verify this matches the intended behavior for your use case, as `hex`/`literal` prefixes may need different handling.
<h3>Confidence Score: 4/5</h3>
- Safe to merge with one verification needed around `hex`/`literal` parameter handling
- The implementation is solid with proper backward compatibility, correct delay logic (skipping after last key), and matching Zod schema to TypeScript types. The sequential sending approach is sound for solving TUI race conditions. Score reduced from 5 to 4 due to one logical concern: `hex` and `literal` parameters are only sent with the first key in delayed mode, which may or may not match the intended behavior depending on the use case.
- Verify that `hex` and `literal` behavior in delayed mode matches your requirements at src/agents/bash-tools.process.ts:500-501
<sub>Last reviewed commit: bb9a321</sub>
<!-- greptile_other_comments_section -->
<sub>(2/5) Greptile learns from your feedback when you react with thumbs up/down!</sub>
<!-- /greptile_comment -->
Most Similar PRs
#20817: feat(process): paced send-keys for PTY sessions
by Operative-001 · 2026-02-19
80.8%
#9220: Fix: TUI drops API responses silently when runID already finalized
by vishaltandale00 · 2026-02-05
72.0%
#20622: feat: add configurable typingTtlSeconds to config schema
by chungjchris · 2026-02-19
71.5%
#16733: fix(ui): avoid injected newlines when tool output is hidden
by jp117 · 2026-02-15
71.5%
#20009: fix(discord): immediately defer interactions to prevent timeouts
by Gitjay11 · 2026-02-18
71.4%
#20406: fix(slack): respect replyToMode when computing statusThreadTs in DMs
by QuinnYates · 2026-02-18
70.9%
#21271: fix(commands): pass channel/capabilities/shell/os to runtime in com...
by evansantos · 2026-02-19
70.8%
#19632: fix: suppressToolErrors now suppresses exec tool failure notifications
by Gitjay11 · 2026-02-18
70.5%
#14309: fix(ui): resolve chat event session key mismatch
by justonlyforyou · 2026-02-11
70.4%
#17552: fix(agents): suppress tool error warnings when assistant already re...
by AytuncYildizli · 2026-02-15
70.4%