Session设置约束
rust-v0.150.0 已移除旧版 config lock 文件的加载、导出和 replay。当前 Session 的配置一致性由 requirements、PermissionProfileState、SessionConfiguration 和设置更新流程共同维护。本文回答一个 实际问题:调用方提交设置时,Codex 如何判断候选值、何时改变运行时、哪些值只影响后续 Turn。
阅读本文前,建议先读 Session依赖装配 了解 Session 如何构造 SessionConfiguration 与 SessionServices,再读 CodexThread公共API 了解 preview、apply 和 Turn input 调用面。本文不展开模型采样和平台沙箱实现。
1. 约束对象
设置输入、内部更新和对外快照是三种不同对象:协议层的 ThreadSettingsOverrides 描述候选修改; Core 的 SessionSettingsUpdate 负责合并字段;SessionConfiguration 通过约束后才成为 Session 的 有效状态;ThreadSettingsSnapshot 只是只读投影。
源码位置:codex-rs/core/src/session/session.rs :: SessionSettingsUpdate, SessionConfiguration::thread_settings_snapshot
#[derive(Default, Clone)]
pub(crate) struct SessionSettingsUpdate {
pub(crate) environments: Option<TurnEnvironmentSelections>,
pub(crate) approval_policy: Option<AskForApproval>,
pub(crate) approvals_reviewer: Option<ApprovalsReviewer>,
pub(crate) permission_profile: Option<PermissionProfile>,
pub(crate) active_permission_profile: Option<ActivePermissionProfile>,
pub(crate) collaboration_mode: Option<CollaborationMode>,
pub(crate) reasoning_summary: Option<ReasoningSummaryConfig>,
pub(crate) service_tier: Option<Option<String>>,
pub(crate) personality: Option<Personality>,
}真实协议类型位于 codex-rs/protocol/src/protocol.rs :: ThreadSettingsSnapshot。快照不是可以原样提交的 锁文件:requirements、当前 environment 和 active permission profile 仍会在 Core 中重新约束。
2. 预览与应用
preview_settings() 只复制并验证候选配置,不改变 Session;update_settings() 才提交新的 SessionConfiguration。应用后,Core 根据差异更新环境选择、标记 MCP runtime dirty、通知 extension config contributors,并在 permission profile 变化时刷新 managed network proxy。
源码位置:codex-rs/core/src/session/mod.rs :: Session::preview_settings, Session::update_settings
pub(crate) async fn preview_settings(
&self,
updates: &SessionSettingsUpdate,
) -> ConstraintResult<ThreadConfigSnapshot> {
let state = self.state.lock().await;
let configuration = self.apply_session_settings(
&state.session_configuration,
updates,
)?;
let environments = updates.environments.as_ref().map_or_else(
|| self.services.turn_environments.selections(),
|value| value.environments.clone(),
);
Ok(configuration.thread_config_snapshot(environments))
}
pub(crate) async fn update_settings(
&self,
updates: SessionSettingsUpdate,
) -> ConstraintResult<()> {
let mut state = self.state.lock().await;
let updated = self.apply_session_settings(
&state.session_configuration,
&updates,
)?;
state.session_configuration = updated;
Ok(())
}真实实现还会在释放状态锁后通知 contributors、刷新环境和 managed proxy。关键边界是:预览不写状态; 应用通过同一 apply_session_settings 约束候选值后才提交。
3. 约束合并
SessionConfiguration::apply 复制旧配置后逐项合并。审批字段调用 Constrained::set;permission profile 重建文件与网络策略并保留已有 deny-read;环境变化经过环境校验;requirements 要求时,切换到不兼容模型 还会强制 AutoReview。
源码位置:codex-rs/core/src/session/session.rs :: SessionConfiguration::apply
if let Some(approval_policy) = updates.approval_policy {
next_configuration.approval_policy.set(approval_policy)?;
}
if let Some(approvals_reviewer) = updates.approvals_reviewer {
next_configuration
.original_config_do_not_use
.config_layer_stack
.requirements()
.approvals_reviewer
.can_set(&approvals_reviewer)?;
next_configuration.approvals_reviewer = approvals_reviewer;
}
if let Some(permission_profile) = updates.permission_profile.clone() {
next_configuration.set_permission_profile_projection(
permission_profile,
updates.active_permission_profile.clone(),
updates.profile_workspace_roots.clone().unwrap_or_default(),
Some(¤t_file_system_sandbox_policy),
)?;
}
next_configuration.validate(next_environments)?;
Ok(next_configuration)这不是单一 allowlist:requirements 可以限制 reviewer 或 model,validate_environment_selections 检查 环境,permission profile projection 还维护文件和网络策略不变量。任一校验失败都保留旧配置。
4. 生效边界
standalone ThreadSettings 和 Turn input 携带的 settings 都经过同一套约束。成功后由 thread_settings.rs 生成完整 ThreadSettingsAppliedEvent;事件内容是更新后的 snapshot,而不是请求 patch 的回声。
源码位置:codex-rs/core/src/session/thread_settings.rs :: update, apply_update, emit_applied
pub(super) async fn apply_update(
session: &Session,
submission_id: String,
updates: SessionSettingsUpdate,
) -> ConstraintResult<()> {
session.update_settings(updates).await?;
emit_applied(session, submission_id).await;
Ok(())
}
pub(super) async fn emit_applied(session: &Session, submission_id: String) {
let msg = applied_event(session).await;
session
.send_event_raw_without_materializing_rollout(Event {
id: submission_id,
msg,
})
.await;
}如果本地 rollout 尚未物化,设置事件不会仅因为设置成功而创建空文件;已有持久化线程仍按正常策略记录。 运行中的 active Turn 保持自己的 TurnContext,新设置主要影响后续 Turn。
5. 失败处理
候选值违反 requirements、permission profile 无法转换或 environment 不合法时,失败发生在状态替换之前; MCP prewarm 和 managed proxy 刷新则只在更新成功后触发。
| 失败位置 | 典型原因 | 原配置是否替换 | 调用方看到什么 |
|---|---|---|---|
Constrained::set | approval policy 不允许 | 否 | BadRequest Error Event |
| requirements reviewer | reviewer 不在允许集合 | 否 | BadRequest Error Event |
| permission profile projection | profile/network 约束无效 | 否 | ConstraintError |
| environment validation | cwd、root 或 environment 选择无效 | 否 | ConstraintError |
| MCP 输入变化 | server 配置需重建 | 是 | runtime dirty + prewarm |
| permission profile 已应用 | managed proxy 需换绑 | 是 | proxy refresh |
ThreadSettings 的错误由 session/thread_settings.rs :: update 转成 EventMsg::Error;TurnInput 的错误 由 TurnInputSubmission::NotSubmitted 或上层 JSON-RPC 错误返回。两者共享约束实现,但完成信号不同。
6. 测试边界
当前版本不再有 config-lock schema/version/replay 测试;测试集中在设置合并、Turn input 路由和权限投影。
源码位置:codex-rs/core/src/session/turn_input_tests.rs :: start_only_rejects_active_turn_without_injecting
let submission = submit_start_only(
&session,
SubmittedTurnInput::ResponseItem(user_message("synthetic idle input")),
)
.await;
assert_eq!(
submission,
TurnInputSubmission::NotSubmitted {
reason: NotSubmittedReason::NotIdle,
}
);
assert_eq!(
session.input_queue.get_pending_input(&session.active_turn).await,
(Vec::<TurnInput>::new(), None, None),
);该测试证明 idle-only 输入在 active Turn 存在时不会注入或排队;它不覆盖所有 requirements 组合。通用约束 容器的失败保持旧值由 codex-rs/config/src/constraint.rs :: Constrained::set 的单元测试覆盖。
7. 继续阅读
要追踪设置从 App Server 进入 Core,可回看 Session依赖装配 与 CodexThread公共API。要理解 permission profile 如何影响文件和网络 强制,转到 Codex信任边界。
rg -n "apply_session_settings|preview_settings|update_settings|ThreadSettingsSnapshot|ConstraintError" \
codex-rs/core/src codex-rs/config/src codex-rs/protocol/src