Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 16 additions & 10 deletions executor/src/runtime_work/handler/collection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -695,7 +695,13 @@ impl RuntimeWorkRpcHandler {
thread_id: String,
turn_id: String,
) {
if !self.is_local_task_execution_active(local_task_id, execution_id) {
let current_execution_id = self
.active_turn_cancellations
.lock()
.expect("active turn cancellation map lock should not be poisoned")
.get(local_task_id)
.map(|control| control.execution_id);
if current_execution_id.is_some_and(|current| current != execution_id) {
return;
}
self.active_codex_turns
Expand Down Expand Up @@ -773,18 +779,18 @@ impl RuntimeWorkRpcHandler {
}

pub(super) fn is_active_local_task(&self, local_task_id: &str) -> bool {
self.active_turn_cancellations
let has_active_execution = self
.active_turn_cancellations
.lock()
.expect("active turn cancellation map lock should not be poisoned")
.contains_key(local_task_id)
}

fn is_local_task_execution_active(&self, local_task_id: &str, execution_id: u64) -> bool {
self.active_turn_cancellations
.contains_key(local_task_id);
if has_active_execution {
return true;
}
self.active_codex_turns
.lock()
.expect("active turn cancellation map lock should not be poisoned")
.get(local_task_id)
.is_some_and(|control| control.execution_id == execution_id)
.expect("active codex turn map lock should not be poisoned")
.contains_key(local_task_id)
}

pub(super) fn clear_active_request_user_input(&self, local_task_id: &str, execution_id: u64) {
Expand Down
48 changes: 48 additions & 0 deletions executor/src/runtime_work/handler/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,54 @@ fn stale_execution_cannot_finish_its_replacement() {
let _ = fs::remove_file(index_path);
}

#[test]
fn provider_turn_registers_when_execution_control_settles_first() {
let handler = RuntimeWorkRpcHandler::new("device-1", "/bin/false");
let execution_id = start_test_execution(&handler, "task-1");

assert!(handler.finish_local_task_execution("task-1", execution_id));
handler.record_active_codex_turn(
"task-1",
execution_id,
"thread-1".to_owned(),
"turn-1".to_owned(),
);

assert!(handler.is_active_local_task("task-1"));

handler.clear_active_codex_turn("task-1", execution_id);
assert!(!handler.is_active_local_task("task-1"));
}

#[test]
fn stale_provider_turn_cannot_replace_the_current_execution() {
let handler = RuntimeWorkRpcHandler::new("device-1", "/bin/false");
let stale_execution_id = start_test_execution(&handler, "task-1");
assert!(handler.finish_local_task_execution("task-1", stale_execution_id));
let current_execution_id = start_test_execution(&handler, "task-1");

handler.record_active_codex_turn(
"task-1",
stale_execution_id,
"stale-thread".to_owned(),
"stale-turn".to_owned(),
);
assert!(handler.active_codex_turn("task-1").is_none());

handler.record_active_codex_turn(
"task-1",
current_execution_id,
"current-thread".to_owned(),
"current-turn".to_owned(),
);
let current_turn = handler
.active_codex_turn("task-1")
.expect("current execution should register its provider turn");
assert_eq!(current_turn.execution_id, current_execution_id);
assert_eq!(current_turn.thread_id, "current-thread");
assert_eq!(current_turn.turn_id, "current-turn");
}

#[test]
fn unlinked_failed_codex_turn_persists_assistant_error_in_runtime_handle() {
let index_path = temp_runtime_work_index_path("persist-failed-assistant");
Expand Down
14 changes: 14 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions wework/e2e/desktop/checkpoints.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export const DESKTOP_CHECKPOINTS = [
'permission-modes',
'core-task-flow',
'runtime-task-queue',
'split-workbench',
'window-lifecycle',
'goal-lifecycle',
'supervisor-lifecycle',
Expand Down
7 changes: 6 additions & 1 deletion wework/e2e/desktop/run-checkpoints.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,14 @@ const CHECKPOINT_SCENARIO_MODULES = {
'browser-multi-tabs': './scenarios/embedded-browser-multi-tabs.scenario.mjs',
'rendering-extensions': './scenarios/streaming-text.scenario.mjs',
'runtime-task-queue': './scenarios/runtime-task-queue.scenario.mjs',
'split-workbench': './scenarios/split-workbench.scenario.mjs',
'project-automation': './scenarios/project-automation.scenario.mjs',
}
const SCENARIO_ONLY_CHECKPOINTS = new Set(['local-harness', 'runtime-task-queue'])
const SCENARIO_ONLY_CHECKPOINTS = new Set([
'local-harness',
'runtime-task-queue',
'split-workbench',
])
const scriptDir = dirname(fileURLToPath(import.meta.url))
const taskFlowPath = join(scriptDir, 'task-flow.e2e.mjs')
const cliArgs = process.argv.slice(2)
Expand Down
8 changes: 6 additions & 2 deletions wework/e2e/desktop/scenarios/local-terminal.scenario.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -368,8 +368,8 @@ async function verifyHarnessWorkbenchChrome({
})
assert.match(
bottomPanelClass,
/(?:^|\s)opacity-100(?:\s|$)/,
`${title} did not apply the open bottom-panel style`
/(?:^|\s)pointer-events-auto(?:\s|$)/,
`${title} did not make the open bottom workspace panel interactive`
)
assert.match(
bottomPanelInlineStyle,
Expand All @@ -380,6 +380,10 @@ async function verifyHarnessWorkbenchChrome({
await control.command('getElementMetrics', bottomPanelSelector)
)
assert.ok(bottomPanelMetrics.width > 0, `${title} opened a zero-width bottom workspace panel`)
assert.ok(
bottomPanelMetrics.height >= 220,
`${title} opened an undersized bottom workspace panel`
)
assert.ok(
bottomPanelMetrics.scrollHeight > 0,
`${title} opened a bottom workspace panel without content`
Expand Down
Loading
Loading