Skip to content

Commit 30a2b30

Browse files
committed
feat: default active steering to batched delivery
1 parent fabfab2 commit 30a2b30

39 files changed

Lines changed: 520 additions & 66 deletions

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ Docs: https://docs.openclaw.ai
77
### Changes
88

99
- Dependencies: refresh workspace runtime, plugin, and tooling packages, including ACP, Pi, AWS SDK, TypeBox, pnpm, oxlint, oxfmt, jsdom, pdfjs, ciao, and tokenjuice, while keeping patched ACP behavior and lint gates current. Thanks @mariozechner.
10+
- Messages/queue: make `steer` drain all pending Pi steering messages at the next model boundary, keep legacy one-at-a-time steering as `queue`, and add a dedicated steering queue docs page. Thanks @vincentkoc.
1011
- Messages/queue: default active-run queueing to `steer` with a 500ms followup fallback debounce, and document the queue modes, precedence, and drop policies on the command queue page. Thanks @vincentkoc.
1112
- Providers/NVIDIA: add the NVIDIA provider with API-key onboarding, setup docs, static catalog metadata, and literal model-ref picker support so NVIDIA hosted models can be selected with their provider prefix intact. (#71204) Thanks @eleqtrizit.
1213
- Memory/wiki: add agent-facing people wiki metadata, canonical aliases, person cards, relationship graphs, privacy/provenance reports, evidence-kind drilldown, and search modes for person lookup, question routing, source evidence, and raw claims. Thanks @vincentkoc.
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
2af6bef21f530dc64e0379f7631bed410aee1d5c86604ef9fb149f546cfcb0e8 config-baseline.json
2-
8d75df355b7f6e44b9c2f195d9df86130beb697e26061469df7d60b7e8a2f204 config-baseline.core.json
1+
dbb3d39ddeb8a9ce03459d69774db7e457d33f47c585e0f8c7130b14b92cfcff config-baseline.json
2+
2197788a6a1e677fb34a4971ac4f254116c243753452397824e08431f8740aab config-baseline.core.json
33
fab66aa304db5697e87259165ad261006719eb6e6cdbd25f957fcba2b7b324e9 config-baseline.channel.json
44
c4231c2194206547af8ad94342dc00aadb734f43cb49cc79d4c46bdbb80c3f95 config-baseline.plugin.json

docs/.i18n/glossary.zh-CN.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,10 @@
5151
"source": "Agent loop",
5252
"target": "Agent loop"
5353
},
54+
{
55+
"source": "Steering queue",
56+
"target": "Steering queue"
57+
},
5458
{
5559
"source": "Models",
5660
"target": "Models"

docs/concepts/agent.md

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -86,13 +86,15 @@ Legacy session folders from other tools are not read.
8686

8787
When queue mode is `steer`, inbound messages are injected into the current run.
8888
Queued steering is delivered **after the current assistant turn finishes
89-
executing its tool calls**, before the next LLM call. Steering no longer skips
90-
remaining tool calls from the current assistant message; it injects the queued
91-
message at the next model boundary instead.
89+
executing its tool calls**, before the next LLM call. Pi drains all pending
90+
steering messages together for `steer`; legacy `queue` drains one message per
91+
model boundary. Steering no longer skips remaining tool calls from the current
92+
assistant message.
9293

9394
When queue mode is `followup` or `collect`, inbound messages are held until the
9495
current turn ends, then a new agent turn starts with the queued payloads. See
95-
[Queue](/concepts/queue) for mode + debounce/cap behavior.
96+
[Queue](/concepts/queue) and [Steering queue](/concepts/queue-steering) for mode
97+
and boundary behavior.
9698

9799
Block streaming sends completed assistant blocks as soon as they finish; it is
98100
**off by default** (`agents.defaults.blockStreamingDefault: "off"`).

docs/concepts/messages.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -127,9 +127,9 @@ current run, or collected for a followup turn.
127127
- Default mode is `steer`, with a 500ms followup debounce when steering falls
128128
back to queued followup delivery.
129129
- Modes: `steer`, `followup`, `collect`, `steer-backlog`, `interrupt`, and the
130-
legacy `queue` alias.
130+
legacy one-at-a-time `queue` mode.
131131

132-
Details: [Command queue](/concepts/queue).
132+
Details: [Command queue](/concepts/queue) and [Steering queue](/concepts/queue-steering).
133133

134134
## Channel run ownership
135135

docs/concepts/queue-steering.md

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
---
2+
summary: "How active-run steering queues messages at runtime boundaries"
3+
read_when:
4+
- Explaining how steer behaves while an agent is using tools
5+
- Changing active-run queue behavior or runtime steering integration
6+
- Comparing steer, queue, collect, and followup modes
7+
title: "Steering queue"
8+
---
9+
10+
When a message arrives while a session run is already streaming, OpenClaw can
11+
send that message into the active runtime instead of starting another run for
12+
the same session. The public modes are runtime-neutral; Pi and the native Codex
13+
app-server harness implement the delivery details differently.
14+
15+
## Runtime boundary
16+
17+
Steering does not interrupt a tool call that is already running. Pi checks for
18+
queued steering messages at model boundaries:
19+
20+
1. The assistant asks for tool calls.
21+
2. Pi executes the current assistant message's tool-call batch.
22+
3. Pi emits the turn end event.
23+
4. Pi drains queued steering messages.
24+
5. Pi appends those messages as user messages before the next LLM call.
25+
26+
This keeps tool results paired with the assistant message that requested them,
27+
then lets the next model call see the latest user input.
28+
29+
The native Codex app-server harness exposes `turn/steer` instead of Pi's
30+
internal steering queue. OpenClaw adapts the same modes there:
31+
32+
- `steer` batches queued messages for the configured quiet window, then sends a
33+
single `turn/steer` request with all collected user input in arrival order.
34+
- `queue` keeps the legacy serialized shape by sending separate `turn/steer`
35+
requests.
36+
- `followup`, `collect`, `steer-backlog`, and `interrupt` stay OpenClaw-owned
37+
queue behavior around the active Codex turn.
38+
39+
Codex review and manual compaction turns reject same-turn steering. When a
40+
runtime cannot accept steering, OpenClaw falls back to the followup queue where
41+
that mode allows it.
42+
43+
## Modes
44+
45+
| Mode | Active-run behavior | Later followup behavior |
46+
| --------------- | ---------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------- |
47+
| `steer` | Injects all queued steering messages together at the next runtime boundary. This is the default. | Falls back to followup only when steering is unavailable. |
48+
| `queue` | Legacy one-at-a-time steering. Pi injects one queued message per model boundary; Codex sends separate `turn/steer` requests. | Falls back to followup only when steering is unavailable. |
49+
| `steer-backlog` | Same active-run steering behavior as `steer`. | Also keeps the same message for a later followup turn. |
50+
| `followup` | Does not steer the current run. | Runs queued messages later. |
51+
| `collect` | Does not steer the current run. | Coalesces compatible queued messages into one later turn after the debounce window. |
52+
| `interrupt` | Aborts the active run, then starts the newest message. | None. |
53+
54+
## Burst example
55+
56+
If four users send messages while the agent is executing a tool call:
57+
58+
- `steer`: the active runtime receives all four messages in arrival order before
59+
its next model decision. Pi drains them at the next model boundary; Codex
60+
receives them as one batched `turn/steer`.
61+
- `queue`: legacy serialized steering. Pi injects one queued message at a time;
62+
Codex receives separate `turn/steer` requests.
63+
- `collect`: OpenClaw waits until the active run ends, then creates a followup
64+
turn with compatible queued messages after the debounce window.
65+
66+
## Scope
67+
68+
Steering always targets the current active session run. It does not create a new
69+
session, change the active run's tool policy, or split messages by sender. In
70+
multi-user channels, inbound prompts already include sender and route context, so
71+
the next model call can see who sent each message.
72+
73+
Use `collect` when you want OpenClaw to build a later followup turn that can
74+
coalesce compatible messages and preserve followup queue drop policy. Use
75+
`queue` only when you need the older one-at-a-time steering behavior.
76+
77+
## Debounce
78+
79+
`messages.queue.debounceMs` applies to followup delivery, including `collect`,
80+
`followup`, `steer-backlog`, and `steer` fallback when active-run steering is not
81+
available. For Pi, active `steer` itself does not use the debounce timer because
82+
Pi naturally batches messages until the next model boundary. For the native
83+
Codex harness, OpenClaw uses the same debounce value as the quiet window before
84+
sending the batched `turn/steer`.
85+
86+
## Related
87+
88+
- [Command queue](/concepts/queue)
89+
- [Messages](/concepts/messages)
90+
- [Agent loop](/concepts/agent-loop)

docs/concepts/queue.md

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,24 +31,28 @@ When unset, all inbound channel surfaces use:
3131
- `drop: "summarize"`
3232

3333
`steer` is the default because it keeps the active model turn responsive without
34-
starting a second session run. If the current run cannot accept steering,
34+
starting a second session run. It drains all steering messages that arrived
35+
before the next model boundary. If the current run cannot accept steering,
3536
OpenClaw falls back to a followup queue entry.
3637

3738
## Queue modes
3839

3940
Inbound messages can steer the current run, wait for a followup turn, or do both:
4041

41-
- `steer`: queue a steering message into the active Pi run. Pi delivers it **after the current assistant turn finishes executing its tool calls**, before the next LLM call. If the run is not actively streaming or steering is unavailable, OpenClaw falls back to a followup queue entry.
42+
- `steer`: queue steering messages into the active runtime. Pi delivers all pending steering messages **after the current assistant turn finishes executing its tool calls**, before the next LLM call; Codex app-server receives one batched `turn/steer`. If the run is not actively streaming or steering is unavailable, OpenClaw falls back to a followup queue entry.
43+
- `queue` (legacy): old one-at-a-time steering. Pi delivers one queued steering message at each model boundary; Codex app-server receives separate `turn/steer` requests. Prefer `steer` unless you need the previous serialized behavior.
4244
- `followup`: enqueue each message for a later agent turn after the current run ends.
4345
- `collect`: coalesce queued messages into a **single** followup turn after the quiet window. If messages target different channels/threads, they drain individually to preserve routing.
4446
- `steer-backlog` (aka `steer+backlog`): steer now **and** preserve the same message for a followup turn.
4547
- `interrupt` (legacy): abort the active run for that session, then run the newest message.
46-
- `queue` (legacy alias): same as `steer`.
4748

4849
Steer-backlog means you can get a followup response after the steered run, so
4950
streaming surfaces can look like duplicates. Prefer `collect`/`steer` if you want
5051
one response per inbound message.
5152

53+
For runtime-specific timing and dependency behavior, see
54+
[Steering queue](/concepts/queue-steering).
55+
5256
Configure globally or per channel via `messages.queue`:
5357

5458
```json5
@@ -67,7 +71,7 @@ Configure globally or per channel via `messages.queue`:
6771

6872
## Queue options
6973

70-
Options apply to `followup`, `collect`, and `steer-backlog` (and to `steer` when it falls back to followup):
74+
Options apply to `followup`, `collect`, and `steer-backlog` (and to `steer` or legacy `queue` when steering falls back to followup):
7175

7276
- `debounceMs`: quiet window before draining queued followups. Bare numbers are milliseconds; units `ms`, `s`, `m`, `h`, and `d` are accepted by `/queue` options.
7377
- `cap`: max queued messages per session. Values below `1` are ignored.
@@ -115,4 +119,5 @@ keys.
115119
## Related
116120

117121
- [Session management](/concepts/session)
122+
- [Steering queue](/concepts/queue-steering)
118123
- [Retry policy](/concepts/retry)

docs/docs.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1164,7 +1164,8 @@
11641164
"concepts/messages",
11651165
"concepts/streaming",
11661166
"concepts/retry",
1167-
"concepts/queue"
1167+
"concepts/queue",
1168+
"concepts/queue-steering"
11681169
]
11691170
}
11701171
]

docs/gateway/config-agents.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1226,7 +1226,7 @@ See [Multi-Agent Sandbox & Tools](/tools/multi-agent-sandbox-tools) for preceden
12261226
ackReactionScope: "group-mentions", // group-mentions | group-all | direct | all
12271227
removeAckAfterReply: false,
12281228
queue: {
1229-
mode: "steer", // steer | followup | collect | steer-backlog | steer+backlog | queue | interrupt
1229+
mode: "steer", // steer | queue (legacy one-at-a-time) | followup | collect | steer-backlog | steer+backlog | interrupt
12301230
debounceMs: 500,
12311231
cap: 20,
12321232
drop: "summarize", // old | new | summarize

docs/help/faq.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1943,13 +1943,14 @@ lives on the [Models FAQ](/help/faq-models).
19431943
<Accordion title='Why does it feel like the bot "ignores" rapid-fire messages?'>
19441944
Queue mode controls how new messages interact with an in-flight run. Use `/queue` to change modes:
19451945

1946-
- `steer` - queue steering for the next model boundary in the current run
1946+
- `steer` - queue all pending steering for the next model boundary in the current run
1947+
- `queue` - legacy one-at-a-time steering
19471948
- `followup` - run messages one at a time
19481949
- `collect` - batch messages and reply once
19491950
- `steer-backlog` - steer now, then process backlog
19501951
- `interrupt` - abort current run and start fresh
19511952

1952-
Default mode is `steer`. You can add options like `debounce:0.5s cap:25 drop:summarize` for followup modes. See [Command queue](/concepts/queue).
1953+
Default mode is `steer`. You can add options like `debounce:0.5s cap:25 drop:summarize` for followup modes. See [Command queue](/concepts/queue) and [Steering queue](/concepts/queue-steering).
19531954

19541955
</Accordion>
19551956
</AccordionGroup>

0 commit comments

Comments
 (0)