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
3 changes: 3 additions & 0 deletions apps/decodex/src/agent/app_server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,7 @@ pub(crate) struct AppServerRunRequest<'a> {
pub(crate) continuation_user_input: Option<String>,
pub(crate) activity_marker_path: Option<PathBuf>,
pub(crate) resume_thread_id: Option<String>,
pub(crate) ephemeral_thread: bool,
pub(crate) command_exec_health_check: Option<CommandExecHealthCheck>,
pub(crate) dynamic_tool_handler: Option<&'a dyn DynamicToolHandler>,
pub(crate) continuation_guard: Option<&'a dyn TurnContinuationGuard>,
Expand Down Expand Up @@ -924,6 +925,7 @@ pub(crate) fn probe_app_server(listen: &str) -> crate::prelude::Result<AppServer
continuation_user_input: None,
activity_marker_path: None,
resume_thread_id: None,
ephemeral_thread: true,
command_exec_health_check: Some(CommandExecHealthCheck::probe()),
dynamic_tool_handler: Some(&probe_tool_handler),
continuation_guard: None,
Expand Down Expand Up @@ -2565,6 +2567,7 @@ fn build_thread_start_request(
cwd: Some(request.cwd.clone()),
dynamic_tools,
developer_instructions: Some(request.developer_instructions.clone()),
ephemeral: request.ephemeral_thread.then_some(true),
..ThreadStartRequest::default()
})
}
Expand Down
16 changes: 16 additions & 0 deletions apps/decodex/src/agent/app_server/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,7 @@ fn thread_start_and_resume_requests_inherit_runtime_config() {
assert!(value.get("approvalPolicy").is_none());
assert!(value.get("sandbox").is_none());
assert!(value.get("config").is_none());
assert!(value.get("ephemeral").is_none());
}

let start =
Expand Down Expand Up @@ -359,13 +360,26 @@ fn minimal_run_request<'a>() -> super::AppServerRunRequest<'a> {
continuation_user_input: None,
activity_marker_path: None,
resume_thread_id: None,
ephemeral_thread: false,
command_exec_health_check: None,
dynamic_tool_handler: None,
continuation_guard: None,
codex_account_provider: None,
}
}

#[test]
fn synthetic_probe_thread_start_is_ephemeral_when_requested() {
let mut request = minimal_run_request();

request.ephemeral_thread = true;

let start = super::build_thread_start_request(&request).expect("request should build");
let value = serde_json::to_value(&start).expect("thread start request should serialize");

assert_eq!(value["ephemeral"], true);
}

#[test]
fn command_exec_health_check_uses_bounded_standalone_request() {
let health_check = CommandExecHealthCheck {
Expand Down Expand Up @@ -1258,6 +1272,7 @@ fn live_app_server_resume_round_trip_updates_marker_and_state() {
)),
activity_marker_path: Some(marker_path.clone()),
resume_thread_id: None,
ephemeral_thread: false,
command_exec_health_check: None,
dynamic_tool_handler: Some(&handler),
continuation_guard: Some(&guard),
Expand Down Expand Up @@ -1301,6 +1316,7 @@ fn live_app_server_resume_round_trip_updates_marker_and_state() {
continuation_user_input: None,
activity_marker_path: Some(marker_path.clone()),
resume_thread_id: Some(first_result.thread_id.clone()),
ephemeral_thread: false,
command_exec_health_check: None,
dynamic_tool_handler: Some(&handler),
continuation_guard: None,
Expand Down
1 change: 1 addition & 0 deletions apps/decodex/src/orchestrator/execution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -452,6 +452,7 @@ where
)),
activity_marker_path: Some(issue_run.worktree.path.clone()),
resume_thread_id: resolve_resume_thread_id(state_store, issue_run)?,
ephemeral_thread: false,
command_exec_health_check: None,
dynamic_tool_handler: Some(&decodex_tool_bridge),
continuation_guard: Some(&continuation_guard),
Expand Down
5 changes: 4 additions & 1 deletion docs/spec/app-server.md
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ The MVP thread start request owns these fields:
- `cwd`
- `dynamicTools` when the run exposes issue-scoped tracker tools
- `developerInstructions`
- `ephemeral` only for synthetic Decodex probe threads

Decodex must not inject project-owned config, model, personality, service-tier, sandbox, or approval-policy overrides into `thread/start`. Child runs inherit runtime defaults from the active Codex runtime.

Expand Down Expand Up @@ -302,6 +303,8 @@ The `decodex probe` command must verify at least:
5. The local client can complete the bounded app-server capability preflight after
`initialize` and before `thread/start`.
6. The local client can complete one bounded standalone `command/exec` health check after `initialize`.
7. The local client can complete one `dynamicTools -> item/tool/call -> response` round trip and still finish with the expected final output.
7. The local client can complete one ephemeral
`dynamicTools -> item/tool/call -> response` round trip and still finish with the
expected final output without materializing a probe thread on disk.

The probe command is the first gate before deeper orchestrator logic depends on the protocol.