Skip to content

Commit 8670c2c

Browse files
Fix subagent completion wakeups
Make terminal subagent completion notifications trigger parent pending work so background agents can hand results back without requiring an explicit wait_agent or close_agent call. Update the nonblocking background execution docs and cover both completion watcher and child-turn forwarding paths. Co-authored-by: Open Codex <hff582580@gmail.com>
1 parent b63cfd6 commit 8670c2c

5 files changed

Lines changed: 27 additions & 6 deletions

File tree

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@ The shared interaction model is:
165165
- the status line keeps foreground `Working` state separate from background subagent/terminal counts.
166166
- terminal details show runtime and recent output; subagent details show role, task, status, runtime, progress, and task-boundary context.
167167
- `/agent` shows available agent profiles, while `/subagents` keeps the subagent thread picker/switching workflow.
168+
- completed subagent work wakes the parent turn through a core completion event, so results can be integrated without manually calling `wait_agent` or closing the agent.
168169
- completed background command output is preserved in history without pulling the task back into the foreground.
169170

170171
This is the essential interaction change behind the Claude Code-style behavior: background work stays visible and controllable, but it no longer blocks normal chat flow.
@@ -455,6 +456,7 @@ Codex CLI 是开源的,但上游仓库当前对外部代码贡献采用 invita
455456
- status line 会把前台 `Working` 状态和后台 subagent/terminal 数量分开显示。
456457
- terminal detail 展示运行时间和最近输出;subagent detail 展示 role、task、status、运行时间、progress 和任务边界信息。
457458
- `/agent` 用于查看可用 agent profiles,`/subagents` 保留 subagent thread picker / 切换工作流。
459+
- subagent 完成后会通过 core completion event 唤醒 parent turn,因此不需要手动调用 `wait_agent` 或关闭 agent 才能继续整合结果。
458460
- background command 完成后会保留输出到 history,但不会把任务重新拉回前台。
459461

460462
这是 Claude Code 风格体验背后的本质交互变化:后台工作仍然可见、可管理,但不会阻塞正常聊天流。

codex-rs/core/src/agent/control.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -999,7 +999,7 @@ impl AgentControl {
999999
parent_agent_path,
10001000
Vec::new(),
10011001
message,
1002-
/*trigger_turn*/ false,
1002+
/*trigger_turn*/ true,
10031003
);
10041004
let _ = control
10051005
.send_inter_agent_communication(parent_thread_id, communication)

codex-rs/core/src/agent/control_tests.rs

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,21 @@ async fn wait_for_subagent_notification(parent_thread: &Arc<CodexThread>) -> boo
198198
timeout(Duration::from_secs(10), wait).await.is_ok()
199199
}
200200

201+
async fn wait_for_turn_started(thread: &Arc<CodexThread>) -> bool {
202+
let wait = async {
203+
loop {
204+
let event = thread
205+
.next_event()
206+
.await
207+
.expect("thread event channel open");
208+
if matches!(event.msg, EventMsg::TurnStarted(_)) {
209+
return true;
210+
}
211+
}
212+
};
213+
timeout(Duration::from_secs(5), wait).await.is_ok()
214+
}
215+
201216
async fn persist_thread_for_tree_resume(thread: &Arc<CodexThread>, message: &str) {
202217
thread
203218
.inject_user_message_without_turn(message.to_string())
@@ -1311,7 +1326,7 @@ async fn multi_agent_v2_completion_ignores_dead_direct_parent() {
13111326
async fn multi_agent_v2_completion_queues_message_for_direct_parent() {
13121327
let harness = AgentControlHarness::new().await;
13131328
let (_root_thread_id, root_thread) = harness.start_thread().await;
1314-
let (worker_thread_id, _worker_thread) = harness.start_thread().await;
1329+
let (worker_thread_id, worker_thread) = harness.start_thread().await;
13151330
let mut tester_config = harness.config.clone();
13161331
let _ = tester_config.features.enable(Feature::MultiAgentV2);
13171332
let tester_thread_id = harness
@@ -1367,7 +1382,7 @@ async fn multi_agent_v2_completion_queues_message_for_direct_parent() {
13671382
worker_path.clone(),
13681383
Vec::new(),
13691384
expected_message.clone(),
1370-
/*trigger_turn*/ false,
1385+
/*trigger_turn*/ true,
13711386
),
13721387
},
13731388
);
@@ -1387,6 +1402,10 @@ async fn multi_agent_v2_completion_queues_message_for_direct_parent() {
13871402
})
13881403
.await
13891404
.expect("completion watcher should queue a direct-parent message");
1405+
assert!(
1406+
wait_for_turn_started(&worker_thread).await,
1407+
"completion watcher should wake the idle direct parent turn"
1408+
);
13901409

13911410
let root_history_items = root_thread
13921411
.codex
@@ -1402,7 +1421,7 @@ async fn multi_agent_v2_completion_queues_message_for_direct_parent() {
14021421
AgentPath::root(),
14031422
Vec::new(),
14041423
expected_message,
1405-
/*trigger_turn*/ false,
1424+
/*trigger_turn*/ true,
14061425
)
14071426
));
14081427
}

codex-rs/core/src/session/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1599,7 +1599,7 @@ impl Session {
15991599
parent_agent_path,
16001600
Vec::new(),
16011601
message,
1602-
/*trigger_turn*/ false,
1602+
/*trigger_turn*/ true,
16031603
);
16041604
if let Err(err) = self
16051605
.services

codex-rs/core/src/tools/handlers/multi_agents_tests.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1474,7 +1474,7 @@ async fn multi_agent_v2_followup_task_completion_notifies_parent_on_every_turn()
14741474
if communication.author == worker_path
14751475
&& communication.recipient == AgentPath::root()
14761476
&& communication.other_recipients.is_empty()
1477-
&& !communication.trigger_turn =>
1477+
&& communication.trigger_turn =>
14781478
{
14791479
Some(communication.content)
14801480
}

0 commit comments

Comments
 (0)