You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat: eager inbox delivery for providers that buffer input during processing
Add opt-in eager delivery mode (CAO_EAGER_INBOX_DELIVERY env var) that
allows inbox messages to be delivered to terminals in PROCESSING or
WAITING_USER_ANSWER states, eliminating the latency gap between agent
turns for capable providers.
Changes:
- Add EAGER_INBOX_DELIVERY constant gated behind CAO_EAGER_INBOX_DELIVERY
- Add accepts_input_while_processing property to BaseProvider (default False)
- Override in ClaudeCodeProvider (returns True only after initialization)
- Relax status gate in check_and_send_pending_messages() with two-flag
check (env var + provider capability)
- Skip idle-pattern pre-check in watchdog for eager-capable providers
- Add native_agent thin-wrapper routing: missing CAO profiles fall back
to --agent <name> for Claude Code's native agent store
- Add 9 unit tests for eager delivery + native agent fallback test
Misc Claude Code provider fixes:
- Preserve CLAUDE_CODE_EFFORT_LEVEL through CAO's env-unset command
- Fix response extraction false stops on ">" in content (Java generics,
git diffs, HTML) by using start-of-line anchor (_SOL_IDLE_RE)
-`permissionMode` (string, `claude_code` only): One of `"default"`, `"acceptEdits"`, `"plan"`, `"auto"`, `"bypassPermissions"`. When set, the `claude_code` provider passes `--permission-mode <value>` instead of `--dangerously-skip-permissions`. `cao launch --yolo` overrides this and forces bypass. See [Claude Code permission modes](https://code.claude.com/docs/en/permission-modes).
36
+
-`native_agent` (string, `claude_code` only): Name of a native Claude Code agent (`~/.claude/agents/`). When set, the provider passes `--agent <name>` directly and skips system prompt / MCP config decomposition (thin-wrapper mode). See [Claude Code native agent routing](claude-code.md#native-agent-routing).
Copy file name to clipboardExpand all lines: docs/claude-code.md
+21Lines changed: 21 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -113,6 +113,27 @@ permissionMode: auto
113
113
You review code for quality and correctness.
114
114
```
115
115
116
+
## Eager Inbox Delivery
117
+
118
+
Claude Code's Ink TUI buffers pasted input even while the agent is processing. CAO exploits this to deliver queued inbox messages during PROCESSING and WAITING_USER_ANSWER states, eliminating inter-turn latency. Enable with `CAO_EAGER_INBOX_DELIVERY=true`.
119
+
120
+
See [Inbox Delivery](inbox-delivery.md) for the full architecture, two-flag gate, and how to enable this for other providers.
121
+
122
+
## Native Agent Routing
123
+
124
+
When a CAO profile specifies a `native_agent` field, the provider passes `--agent <name>` directly to Claude Code's native agent store (`~/.claude/agents/`). This is a thin-wrapper mode where Claude Code handles all configuration (MCP servers, hooks, tools, model).
125
+
126
+
If no CAO profile is found for the given agent name, the provider also falls back to `--agent <name>`, assuming it exists in the native store.
127
+
128
+
```markdown
129
+
---
130
+
name: my-wrapper
131
+
description: Thin wrapper for a native Claude Code agent
132
+
provider: claude_code
133
+
native_agent: my-native-agent
134
+
---
135
+
```
136
+
116
137
## Implementation Notes
117
138
118
139
-**Prompt patterns**: `IDLE_PROMPT_PATTERN` matches both old `>` and new `❯` prompt styles, including non-breaking space (`\xa0`)
When an agent calls `send_message(terminal_id, message)`, the message is queued in the database and delivered to the target terminal's input area via bracketed paste. Delivery has two paths:
6
+
7
+
1.**Immediate**: the API endpoint attempts delivery right after persisting the message
8
+
2.**Watchdog**: a `PollingObserver` (5s interval) monitors terminal log files for changes and attempts delivery when idle patterns are detected
9
+
10
+
Both paths converge on `check_and_send_pending_messages()`, which gates delivery based on terminal status.
11
+
12
+
## Standard Delivery
13
+
14
+
By default, messages are only delivered when the terminal status is **IDLE** or **COMPLETED**. This ensures the provider's TUI is ready to accept input and the message won't be lost or corrupt the terminal state.
15
+
16
+
## Eager Delivery
17
+
18
+
Some providers (e.g., Claude Code) have TUIs that buffer pasted input even while processing. For these providers, waiting for IDLE introduces unnecessary latency between agent turns.
19
+
20
+
Eager delivery allows messages to be delivered during **PROCESSING** and **WAITING_USER_ANSWER** states, eliminating the inter-turn gap.
21
+
22
+
### Enabling
23
+
24
+
Set the environment variable before starting the CAO server:
25
+
26
+
```bash
27
+
export CAO_EAGER_INBOX_DELIVERY=true
28
+
cao-server
29
+
```
30
+
31
+
When disabled (default), delivery behavior is unchanged -- messages wait for IDLE or COMPLETED.
32
+
33
+
### Two-Flag Gate
34
+
35
+
Eager delivery requires both conditions to be true:
36
+
37
+
1.**Environment variable** (`CAO_EAGER_INBOX_DELIVERY=true`): global kill-switch for operators
This prevents accidental delivery to providers whose TUIs would be corrupted by unsolicited input during processing.
41
+
42
+
### How the Watchdog Path Changes
43
+
44
+
Without eager delivery, the watchdog uses a fast `_has_idle_pattern()` check before attempting delivery. For eager-capable providers, this check is skipped (there is no idle pattern during PROCESSING), and the watchdog proceeds directly to `check_and_send_pending_messages()` where the full status gate applies.
A property on `BaseProvider` (default `False`) that signals whether a provider's TUI safely buffers pasted input during processing. Override to `True` in providers that support this.
49
+
50
+
Currently enabled for:
51
+
-**Claude Code** (`ClaudeCodeProvider`): Ink TUI buffers input at all times
52
+
53
+
Other providers that may support this (contributions welcome):
54
+
-**Codex**: TUI-based, may buffer input
55
+
-**OpenCode**: TUI-based, may buffer input
56
+
57
+
To enable for a new provider, override the property:
58
+
59
+
```python
60
+
@property
61
+
defaccepts_input_while_processing(self) -> bool:
62
+
"""This provider buffers pasted input during processing."""
63
+
returnself._initialized
64
+
```
65
+
66
+
The `_initialized` gate is important -- it prevents delivery during startup when `get_status()` returns PROCESSING but the REPL isn't actually ready.
67
+
68
+
### Risks
69
+
70
+
| Risk | Likelihood | Mitigation |
71
+
|------|-----------|------------|
72
+
| Message delivered during PROCESSING gets lost (agent errors mid-turn) | Low | Message status is DELIVERED; acceptable for v1 |
73
+
| Watchdog fires every 5s during long turns | Medium (bounded) | One DB query + one tmux call per interval; no amplification |
74
+
| Feature causes regression in non-eager providers | None | Provider flag defaults to False; only opt-in providers affected |
0 commit comments