forked from openai/codex
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmod.rs
More file actions
3740 lines (3446 loc) · 149 KB
/
Copy pathmod.rs
File metadata and controls
3740 lines (3446 loc) · 149 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
use crate::agents_md::AgentsMdManager;
use crate::config::edit::ConfigEdit;
use crate::config::edit::ConfigEditsBuilder;
use crate::path_utils::normalize_for_native_workdir;
use crate::unified_exec::DEFAULT_MAX_BACKGROUND_TERMINAL_TIMEOUT_MS;
use crate::unified_exec::MIN_EMPTY_YIELD_TIME_MS;
use crate::windows_sandbox::WindowsSandboxLevelExt;
use crate::windows_sandbox::resolve_windows_sandbox_mode;
use crate::windows_sandbox::resolve_windows_sandbox_private_desktop;
use codex_config::CloudRequirementsLoader;
use codex_config::ConfigLayerSource;
use codex_config::ConfigLayerStack;
use codex_config::ConfigLayerStackOrdering;
use codex_config::ConfigRequirements;
use codex_config::ConfigRequirementsToml;
use codex_config::ConstrainedWithSource;
use codex_config::FeatureRequirementsToml;
use codex_config::McpServerIdentity;
use codex_config::McpServerRequirement;
use codex_config::PluginRequirementsToml;
use codex_config::ProfileV2Name;
use codex_config::ResidencyRequirement;
use codex_config::SandboxModeRequirement;
use codex_config::Sourced;
use codex_config::ThreadConfigLoader;
use codex_config::config_toml::ConfigLockfileToml;
use codex_config::config_toml::ConfigToml;
use codex_config::config_toml::DEFAULT_PROJECT_DOC_MAX_BYTES;
use codex_config::config_toml::ProjectConfig;
use codex_config::config_toml::RealtimeAudioConfig;
use codex_config::config_toml::RealtimeConfig;
use codex_config::config_toml::ThreadStoreToml;
use codex_config::config_toml::validate_model_providers;
use codex_config::loader::load_config_layers_state;
use codex_config::loader::project_trust_key;
use codex_config::profile_toml::ConfigProfile;
use codex_config::sandbox_mode_requirement_for_permission_profile;
use codex_config::types::ApprovalsReviewer;
use codex_config::types::AuthCredentialsStoreMode;
use codex_config::types::History;
use codex_config::types::McpServerConfig;
use codex_config::types::McpServerDisabledReason;
use codex_config::types::McpServerTransportConfig;
use codex_config::types::MemoriesConfig;
use codex_config::types::ModelAvailabilityNuxConfig;
use codex_config::types::Notice;
use codex_config::types::OAuthCredentialsStoreMode;
use codex_config::types::SessionPickerViewMode;
use codex_config::types::ToolSuggestConfig;
use codex_config::types::ToolSuggestDisabledTool;
use codex_config::types::ToolSuggestDiscoverable;
use codex_config::types::TuiKeymap;
use codex_config::types::TuiNotificationSettings;
use codex_config::types::TuiPetAnchor;
use codex_config::types::UriBasedFileOpener;
use codex_config::types::WindowsSandboxModeToml;
use codex_core_plugins::PluginsConfigInput;
use codex_exec_server::ExecutorFileSystem;
use codex_exec_server::LOCAL_FS;
use codex_features::AppsMcpPathOverrideConfigToml;
use codex_features::Feature;
use codex_features::FeatureConfigSource;
use codex_features::FeatureOverrides;
use codex_features::FeatureToml;
use codex_features::Features;
use codex_features::FeaturesToml;
use codex_features::MultiAgentV2ConfigToml;
use codex_features::NetworkProxyConfigToml;
use codex_git_utils::resolve_root_git_project_for_trust;
use codex_login::AuthManagerConfig;
use codex_mcp::McpConfig;
use codex_memories_read::memory_root;
use codex_model_provider_info::LEGACY_OLLAMA_CHAT_PROVIDER_ID;
use codex_model_provider_info::ModelProviderInfo;
use codex_model_provider_info::OLLAMA_CHAT_PROVIDER_REMOVED_ERROR;
use codex_model_provider_info::built_in_model_providers;
use codex_model_provider_info::merge_configured_model_providers;
use codex_models_manager::ModelsManagerConfig;
use codex_protocol::config_types::AltScreenMode;
use codex_protocol::config_types::AutoCompactTokenLimitScope;
use codex_protocol::config_types::ForcedLoginMethod;
use codex_protocol::config_types::Personality;
use codex_protocol::config_types::ReasoningSummary;
use codex_protocol::config_types::SandboxMode;
use codex_protocol::config_types::ServiceTier;
use codex_protocol::config_types::ShellEnvironmentPolicy;
use codex_protocol::config_types::TrustLevel;
use codex_protocol::config_types::Verbosity;
use codex_protocol::config_types::WebSearchConfig;
use codex_protocol::config_types::WebSearchMode;
use codex_protocol::config_types::WindowsSandboxLevel;
use codex_protocol::models::ActivePermissionProfile;
use codex_protocol::models::PermissionProfile;
use codex_protocol::models::SandboxEnforcement;
use codex_protocol::openai_models::ModelsResponse;
use codex_protocol::openai_models::ReasoningEffort;
use codex_protocol::permissions::FileSystemSandboxPolicy;
use codex_protocol::permissions::NetworkSandboxPolicy;
use codex_protocol::protocol::AskForApproval;
use codex_protocol::protocol::SandboxPolicy;
use codex_utils_absolute_path::AbsolutePathBuf;
use codex_utils_absolute_path::AbsolutePathBufGuard;
use rmcp::model::ElicitationCapability;
use rmcp::model::FormElicitationCapability;
use rmcp::model::UrlElicitationCapability;
use serde::Deserialize;
use serde::Serialize;
use std::collections::BTreeMap;
use std::collections::HashMap;
use std::collections::HashSet;
use std::collections::hash_map::Entry;
use std::io::ErrorKind;
use std::path::Path;
use std::path::PathBuf;
use std::sync::Arc;
use crate::config::permissions::BUILT_IN_WORKSPACE_PROFILE;
use crate::config::permissions::apply_network_proxy_feature_config;
use crate::config::permissions::builtin_permission_profile;
use crate::config::permissions::compile_permission_profile_selection;
use crate::config::permissions::compile_permission_profile_workspace_roots;
use crate::config::permissions::default_builtin_permission_profile_name;
use crate::config::permissions::get_readable_roots_required_for_codex_runtime;
use crate::config::permissions::network_proxy_config_for_profile_selection;
use crate::config::permissions::validate_user_permission_profile_names;
use crate::config_lock::config_without_lock_controls;
use crate::config_lock::lock_layer_from_config;
use crate::config_lock::read_config_lock_from_path;
use codex_network_proxy::NetworkProxyConfig;
use toml::Value as TomlValue;
use toml_edit::DocumentMut;
pub(crate) mod agent_roles;
pub mod edit;
mod managed_features;
mod network_proxy_spec;
mod otel;
mod permissions;
mod resolved_permission_profile;
#[cfg(test)]
mod schema;
pub use codex_config::ConfigLoadOptions;
pub use codex_config::Constrained;
pub use codex_config::ConstraintError;
pub use codex_config::ConstraintResult;
pub use codex_config::LoaderOverrides;
pub use codex_network_proxy::NetworkProxyAuditMetadata;
use codex_sandboxing::compatibility_sandbox_policy_for_permission_profile;
pub use codex_sandboxing::system_bwrap_warning;
pub use managed_features::ManagedFeatures;
pub use network_proxy_spec::NetworkProxySpec;
pub use network_proxy_spec::StartedNetworkProxy;
pub(crate) use permissions::resolve_permission_profile;
pub use resolved_permission_profile::PermissionProfileSnapshot;
pub(crate) use resolved_permission_profile::PermissionProfileState;
const DEFAULT_IGNORE_LARGE_UNTRACKED_DIRS: i64 = 200;
const DEFAULT_IGNORE_LARGE_UNTRACKED_FILES: i64 = 10 * 1024 * 1024;
/// Compatibility-only config retained so legacy `ghost_snapshot` settings
/// continue to load even though snapshots are no longer produced.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct GhostSnapshotConfig {
pub ignore_large_untracked_files: Option<i64>,
pub ignore_large_untracked_dirs: Option<i64>,
pub disable_warnings: bool,
}
impl Default for GhostSnapshotConfig {
fn default() -> Self {
Self {
ignore_large_untracked_files: Some(DEFAULT_IGNORE_LARGE_UNTRACKED_FILES),
ignore_large_untracked_dirs: Some(DEFAULT_IGNORE_LARGE_UNTRACKED_DIRS),
disable_warnings: false,
}
}
}
/// Maximum number of bytes of the documentation that will be embedded. Larger
/// files are *silently truncated* to this size so we do not take up too much of
/// the context window.
pub(crate) const AGENTS_MD_MAX_BYTES: usize = DEFAULT_PROJECT_DOC_MAX_BYTES; // 32 KiB
pub(crate) const DEFAULT_AGENT_MAX_THREADS: Option<usize> = Some(6);
pub(crate) const DEFAULT_MULTI_AGENT_V2_MAX_CONCURRENT_THREADS_PER_SESSION: usize = 4;
pub(crate) const DEFAULT_MULTI_AGENT_V2_MIN_WAIT_TIMEOUT_MS: i64 = 10_000;
pub(crate) const DEFAULT_MULTI_AGENT_V2_MAX_WAIT_TIMEOUT_MS: i64 = 3600 * 1000;
pub(crate) const DEFAULT_MULTI_AGENT_V2_DEFAULT_WAIT_TIMEOUT_MS: i64 = 30_000;
pub(crate) const HARD_MIN_MULTI_AGENT_V2_TIMEOUT_MS: i64 = 0;
pub(crate) const HARD_MAX_MULTI_AGENT_V2_TIMEOUT_MS: i64 =
DEFAULT_MULTI_AGENT_V2_MAX_WAIT_TIMEOUT_MS;
pub(crate) const DEFAULT_AGENT_MAX_DEPTH: i32 = 1;
pub(crate) const DEFAULT_AGENT_JOB_MAX_RUNTIME_SECONDS: Option<u64> = None;
const LOCAL_DEV_BUILD_VERSION: &str = "0.0.0";
pub const CONFIG_TOML_FILE: &str = "config.toml";
const CONFIG_PROFILE_V2_SUFFIX: &str = ".config.toml";
fn resolve_sqlite_home_env(resolved_cwd: &Path) -> Option<PathBuf> {
let raw = std::env::var(codex_state::SQLITE_HOME_ENV).ok()?;
let trimmed = raw.trim();
if trimmed.is_empty() {
return None;
}
let path = PathBuf::from(trimmed);
if path.is_absolute() {
Some(path)
} else {
Some(resolved_cwd.join(path))
}
}
fn resolve_cli_auth_credentials_store_mode(
configured: AuthCredentialsStoreMode,
package_version: &str,
) -> AuthCredentialsStoreMode {
match (package_version, configured) {
(
LOCAL_DEV_BUILD_VERSION,
AuthCredentialsStoreMode::Keyring | AuthCredentialsStoreMode::Auto,
) => AuthCredentialsStoreMode::File,
(_, mode) => mode,
}
}
fn resolve_mcp_oauth_credentials_store_mode(
configured: OAuthCredentialsStoreMode,
package_version: &str,
) -> OAuthCredentialsStoreMode {
match (package_version, configured) {
(
LOCAL_DEV_BUILD_VERSION,
OAuthCredentialsStoreMode::Keyring | OAuthCredentialsStoreMode::Auto,
) => OAuthCredentialsStoreMode::File,
(_, mode) => mode,
}
}
#[cfg(test)]
pub(crate) async fn test_config() -> Config {
let codex_home = tempfile::tempdir().expect("create temp dir");
Config::load_from_base_config_with_overrides(
ConfigToml::default(),
ConfigOverrides::default(),
AbsolutePathBuf::from_absolute_path(codex_home.path()).expect("temp dir should resolve"),
)
.await
.expect("load default test config")
}
/// Application configuration loaded from disk and merged with overrides.
#[derive(Debug, Clone, PartialEq)]
pub struct Permissions {
/// Approval policy for executing commands.
pub approval_policy: Constrained<AskForApproval>,
/// Constrained permission profile plus its selected profile identity, if
/// the profile came from a built-in or named config profile.
permission_profile_state: PermissionProfileState,
/// Thread-scoped runtime workspace roots. Symbolic `:workspace_roots`
/// entries in the permission profile are materialized against these roots.
workspace_roots: Vec<AbsolutePathBuf>,
/// Effective network configuration applied to all spawned processes.
pub network: Option<NetworkProxySpec>,
/// Whether the model may request a login shell for shell-based tools.
/// Default to `true`
///
/// If `true`, the model may request a login shell (`login = true`), and
/// omitting `login` defaults to using a login shell.
/// If `false`, the model can never use a login shell: `login = true`
/// requests are rejected, and omitting `login` defaults to a non-login
/// shell.
pub allow_login_shell: bool,
/// Policy used to build process environments for shell/unified exec.
pub shell_environment_policy: ShellEnvironmentPolicy,
/// Effective Windows sandbox mode derived from `[windows].sandbox` or
/// legacy feature keys.
pub windows_sandbox_mode: Option<WindowsSandboxModeToml>,
/// Whether the final Windows sandboxed child should run on a private desktop.
pub windows_sandbox_private_desktop: bool,
}
impl Permissions {
/// Build permissions from the constrained values required for a minimal
/// in-process configuration.
pub fn from_approval_and_profile(
approval_policy: Constrained<AskForApproval>,
permission_profile: Constrained<PermissionProfile>,
) -> ConstraintResult<Self> {
Ok(Self {
approval_policy,
permission_profile_state: PermissionProfileState::from_constrained_legacy(
permission_profile,
)?,
workspace_roots: Vec::new(),
network: None,
allow_login_shell: true,
shell_environment_policy: ShellEnvironmentPolicy::default(),
windows_sandbox_mode: None,
windows_sandbox_private_desktop: true,
})
}
pub(crate) fn permission_profile_state(&self) -> &PermissionProfileState {
&self.permission_profile_state
}
pub(crate) fn set_permission_profile_state(
&mut self,
permission_profile_state: PermissionProfileState,
) {
self.permission_profile_state = permission_profile_state;
}
/// Apply a permission profile snapshot emitted by core session state.
///
/// This is a trusted-state bridge for consumers of `SessionConfigured`.
/// Config loading and app-server selection should resolve named profiles
/// through config instead of constructing a snapshot directly.
pub fn set_permission_profile_from_session_snapshot(
&mut self,
snapshot: PermissionProfileSnapshot,
) -> ConstraintResult<()> {
self.permission_profile_state
.set_permission_profile_snapshot(snapshot)
}
/// Replace the current permission constraints with a trusted session
/// snapshot. This is only for clients that must mirror core session state
/// after their local config constraints reject the snapshot.
pub fn replace_permission_profile_from_session_snapshot(
&mut self,
snapshot: PermissionProfileSnapshot,
) -> ConstraintResult<()> {
let permission_profile = Constrained::allow_only(snapshot.permission_profile().clone());
self.permission_profile_state = PermissionProfileState::from_constrained_resolved(
permission_profile,
snapshot.into_resolved_permission_profile(),
)?;
Ok(())
}
/// Borrow the canonical profile before runtime workspace-root
/// materialization has been applied.
pub fn permission_profile(&self) -> &PermissionProfile {
self.permission_profile_state.permission_profile()
}
pub fn can_set_permission_profile(
&self,
permission_profile: &PermissionProfile,
) -> ConstraintResult<()> {
self.permission_profile_state
.can_set_legacy_permission_profile(permission_profile)
}
pub fn set_workspace_roots(&mut self, workspace_roots: Vec<AbsolutePathBuf>) {
self.workspace_roots = workspace_roots;
}
pub fn workspace_roots(&self) -> &[AbsolutePathBuf] {
&self.workspace_roots
}
/// Workspace roots that came from user-visible configuration or runtime
/// selection. Internal Codex-only writable roots are intentionally excluded.
pub fn user_visible_workspace_roots(&self) -> &[AbsolutePathBuf] {
&self.workspace_roots
}
pub fn profile_workspace_roots(&self) -> &[AbsolutePathBuf] {
self.permission_profile_state.profile_workspace_roots()
}
fn materialized_permission_profile(&self) -> PermissionProfile {
self.permission_profile()
.clone()
.materialize_project_roots_with_workspace_roots(&self.workspace_roots)
}
/// Effective runtime permissions after config requirements and runtime
/// workspace-root materialization have been applied.
pub fn effective_permission_profile(&self) -> PermissionProfile {
self.materialized_permission_profile()
}
/// Named profile selected by config, if the current profile has one.
pub fn active_permission_profile(&self) -> Option<ActivePermissionProfile> {
self.permission_profile_state.active_permission_profile()
}
/// Effective filesystem sandbox policy derived from the canonical profile.
pub fn file_system_sandbox_policy(&self) -> FileSystemSandboxPolicy {
self.materialized_permission_profile()
.file_system_sandbox_policy()
}
/// Effective network sandbox policy derived from the canonical profile.
pub fn network_sandbox_policy(&self) -> NetworkSandboxPolicy {
self.permission_profile().network_sandbox_policy()
}
/// Legacy compatibility projection derived from the canonical profile.
pub fn legacy_sandbox_policy(&self, cwd: &Path) -> SandboxPolicy {
let permission_profile = self.materialized_permission_profile();
let file_system_sandbox_policy = permission_profile.file_system_sandbox_policy();
compatibility_sandbox_policy_for_permission_profile(
&permission_profile,
&file_system_sandbox_policy,
permission_profile.network_sandbox_policy(),
cwd,
)
}
/// Check whether a legacy sandbox policy can be applied to this permission
/// set after projecting it into the canonical permission profile.
pub fn can_set_legacy_sandbox_policy(
&self,
sandbox_policy: &SandboxPolicy,
cwd: &Path,
) -> ConstraintResult<()> {
let file_system_sandbox_policy =
FileSystemSandboxPolicy::from_legacy_sandbox_policy_for_cwd(sandbox_policy, cwd);
let network_sandbox_policy = NetworkSandboxPolicy::from(sandbox_policy);
let permission_profile = PermissionProfile::from_runtime_permissions_with_enforcement(
SandboxEnforcement::from_legacy_sandbox_policy(sandbox_policy),
&file_system_sandbox_policy,
network_sandbox_policy,
);
self.permission_profile_state
.can_set_legacy_permission_profile(&permission_profile)
}
/// Set permissions from a legacy sandbox policy and keep every permission
/// projection in sync.
pub fn set_legacy_sandbox_policy(
&mut self,
sandbox_policy: SandboxPolicy,
cwd: &Path,
) -> ConstraintResult<()> {
self.can_set_legacy_sandbox_policy(&sandbox_policy, cwd)?;
let file_system_sandbox_policy =
FileSystemSandboxPolicy::from_legacy_sandbox_policy_for_cwd(&sandbox_policy, cwd);
let network_sandbox_policy = NetworkSandboxPolicy::from(&sandbox_policy);
let permission_profile = PermissionProfile::from_runtime_permissions_with_enforcement(
SandboxEnforcement::from_legacy_sandbox_policy(&sandbox_policy),
&file_system_sandbox_policy,
network_sandbox_policy,
);
self.workspace_roots = match &sandbox_policy {
SandboxPolicy::WorkspaceWrite { writable_roots, .. } => {
let mut workspace_roots = vec![
AbsolutePathBuf::from_absolute_path(cwd)
.unwrap_or_else(|_| AbsolutePathBuf::resolve_path_against_base(cwd, "/")),
];
for root in writable_roots {
if !workspace_roots.iter().any(|existing| existing == root) {
workspace_roots.push(root.clone());
}
}
workspace_roots
}
SandboxPolicy::DangerFullAccess
| SandboxPolicy::ExternalSandbox { .. }
| SandboxPolicy::ReadOnly { .. } => vec![
AbsolutePathBuf::from_absolute_path(cwd)
.unwrap_or_else(|_| AbsolutePathBuf::resolve_path_against_base(cwd, "/")),
],
};
self.permission_profile_state
.set_legacy_permission_profile(permission_profile)?;
Ok(())
}
/// Set permissions from the canonical profile.
pub fn set_permission_profile(
&mut self,
permission_profile: PermissionProfile,
) -> ConstraintResult<()> {
self.permission_profile_state
.set_legacy_permission_profile(permission_profile)
}
}
// A profile override only inherits the selected profile's proxy/allowlist config
// when Codex is still responsible for the network policy. `Disabled` means no
// outer sandbox, so starting the managed proxy would narrow the override.
fn profile_allows_configured_network_proxy(permission_profile: &PermissionProfile) -> bool {
match permission_profile {
PermissionProfile::Managed { network, .. } | PermissionProfile::External { network } => {
network.is_enabled()
}
PermissionProfile::Disabled => false,
}
}
/// Configured thread persistence backend.
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub enum ThreadStoreConfig {
/// Persist threads locally using rollout JSONL files and sqlite metadata.
#[default]
Local,
/// In-memory thread store for test and debug configurations.
InMemory { id: String },
}
/// Application configuration loaded from disk and merged with overrides.
#[derive(Debug, Clone, PartialEq)]
pub struct Config {
/// Provenance for how this [`Config`] was derived (merged layers + enforced
/// requirements).
pub config_layer_stack: ConfigLayerStack,
/// Warnings collected during config load that should be shown on startup.
pub startup_warnings: Vec<String>,
/// Optional override of model selection.
pub model: Option<String>,
/// Effective service tier request id preference for new turns.
pub service_tier: Option<String>,
/// Model used specifically for review sessions.
pub review_model: Option<String>,
/// Size of the context window for the model, in tokens.
pub model_context_window: Option<i64>,
/// Token usage threshold triggering auto-compaction of conversation history.
pub model_auto_compact_token_limit: Option<i64>,
/// Controls whether `model_auto_compact_token_limit` applies to the full
/// active context or only tokens after the carried compaction-window prefix.
pub model_auto_compact_token_limit_scope: AutoCompactTokenLimitScope,
/// Key into the model_providers map that specifies which provider to use.
pub model_provider_id: String,
/// Info needed to make an API request to the model.
pub model_provider: ModelProviderInfo,
/// Optionally specify the personality of the model
pub personality: Option<Personality>,
/// Effective permission configuration for shell tool execution.
pub permissions: Permissions,
/// Whether config explicitly selected named permissions profiles instead
/// of the legacy `sandbox_mode` syntax.
pub explicit_permission_profile_mode: bool,
/// User-defined permission profile IDs available from effective config.
pub custom_permission_profile_ids: Vec<String>,
/// Configures who approval requests are routed to for review once they have
/// been escalated. This does not disable separate safety checks such as
/// ARC.
pub approvals_reviewer: ApprovalsReviewer,
/// enforce_residency means web traffic cannot be routed outside of a
/// particular geography. HTTP clients should direct their requests
/// using backend-specific headers or URLs to enforce this.
pub enforce_residency: Constrained<Option<ResidencyRequirement>>,
/// When `true`, `AgentReasoning` events emitted by the backend will be
/// suppressed from the frontend output. This can reduce visual noise when
/// users are only interested in the final agent responses.
pub hide_agent_reasoning: bool,
/// When set to `true`, `AgentReasoningRawContentEvent` events will be shown in the UI/output.
/// Defaults to `false`.
pub show_raw_agent_reasoning: bool,
/// User-provided instructions from AGENTS.md.
pub user_instructions: Option<String>,
/// Base instructions override.
pub base_instructions: Option<String>,
/// Developer instructions override injected as a separate message.
pub developer_instructions: Option<String>,
/// Guardian-specific policy config override from requirements.toml or config.toml.
/// This is inserted into the fixed guardian prompt template under the
/// `# Policy Configuration` section rather than replacing the whole
/// guardian developer prompt.
pub guardian_policy_config: Option<String>,
/// Whether to inject the `<permissions instructions>` developer block.
pub include_permissions_instructions: bool,
/// Whether to inject the `<apps_instructions>` developer block.
pub include_apps_instructions: bool,
/// Whether to inject the `<collaboration_mode>` developer block.
pub include_collaboration_mode_instructions: bool,
/// Whether to inject the `<skills_instructions>` developer block.
pub include_skill_instructions: bool,
/// Whether the automatic skills instructions include guidance to announce skill usage.
pub skill_announce_usage: bool,
/// Whether to inject the `<environment_context>` user block.
pub include_environment_context: bool,
/// Compact prompt override.
pub compact_prompt: Option<String>,
/// Optional external notifier command. When set, Codex will spawn this
/// program after each completed *turn* (i.e. when the agent finishes
/// processing a user submission). The value must be the full command
/// broken into argv tokens **without** the trailing JSON argument - Codex
/// appends one extra argument containing a JSON payload describing the
/// event.
///
/// Example `~/.codex/config.toml` snippet:
///
/// ```toml
/// notify = ["notify-send", "Codex"]
/// ```
///
/// which will be invoked as:
///
/// ```shell
/// notify-send Codex '{"type":"agent-turn-complete","turn-id":"12345"}'
/// ```
///
/// If unset the feature is disabled.
pub notify: Option<Vec<String>>,
/// TUI notification settings, including enabled events, delivery method, and focus condition.
pub tui_notifications: TuiNotificationSettings,
/// Enable ASCII animations and shimmer effects in the TUI.
pub animations: bool,
/// Show startup tooltips in the TUI welcome screen.
pub show_tooltips: bool,
/// Persisted startup availability NUX state for model tooltips.
pub model_availability_nux: ModelAvailabilityNuxConfig,
/// Start the composer in Vim mode (`Normal`) by default.
pub tui_vim_mode_default: bool,
/// Start the TUI in raw scrollback mode for copy-friendly transcript output.
pub tui_raw_output_mode: bool,
/// Start the TUI in the specified collaboration mode (plan/default).
/// Controls whether the TUI uses the terminal's alternate screen buffer.
///
/// This is the same `tui.alternate_screen` value from `config.toml`.
/// - `auto` (default): Use alternate screen.
/// - `always`: Always use alternate screen.
/// - `never`: Never use alternate screen (inline mode, preserves scrollback).
pub tui_alternate_screen: AltScreenMode,
/// Ordered list of status line item identifiers for the TUI.
///
/// When unset, the TUI defaults to: `model-with-reasoning` and `current-dir`.
pub tui_status_line: Option<Vec<String>>,
/// Whether to color status line items with colors from the active syntax theme.
pub tui_status_line_use_colors: bool,
/// Ordered list of terminal title item identifiers for the TUI.
///
/// When unset, the TUI defaults to: `activity` and `project`.
/// The `activity` item spins while working and shows an action-required
/// message when blocked on the user.
pub tui_terminal_title: Option<Vec<String>>,
/// Syntax highlighting theme override (kebab-case name).
pub tui_theme: Option<String>,
/// Pet id preselected by the terminal pet picker.
pub tui_pet: Option<String>,
/// Vertical anchor used by terminal pet rendering.
pub tui_pet_anchor: TuiPetAnchor,
/// Preferred layout for resume/fork session picker results.
pub tui_session_picker_view: SessionPickerViewMode,
/// Terminal resize-reflow tuning knobs.
pub terminal_resize_reflow: TerminalResizeReflowConfig,
/// Keybinding overrides for the TUI.
///
/// Precedence is:
///
/// 1. context table (`tui.keymap.chat`, `tui.keymap.composer`, etc.)
/// 2. `tui.keymap.global`
/// 3. built-in defaults
pub tui_keymap: TuiKeymap,
/// The absolute directory that should be treated as the current working
/// directory for the session. All relative paths inside the business-logic
/// layer are resolved against this path.
pub cwd: AbsolutePathBuf,
/// Absolute runtime workspace roots for the session. Symbolic
/// `:workspace_roots` permission entries are materialized against these
/// roots while profile-defined workspace roots remain encoded directly in
/// the permission profile.
pub workspace_roots: Vec<AbsolutePathBuf>,
/// Whether runtime workspace roots were supplied explicitly by the caller
/// or legacy config, rather than defaulting to `cwd`.
pub workspace_roots_explicit: bool,
/// Preferred store for CLI auth credentials.
/// file (default): Use a file in the Codex home directory.
/// keyring: Use an OS-specific keyring service.
/// auto: Use the OS-specific keyring service if available, otherwise use a file.
pub cli_auth_credentials_store_mode: AuthCredentialsStoreMode,
/// Definition for MCP servers that Codex can reach out to for tool calls.
pub mcp_servers: Constrained<HashMap<String, McpServerConfig>>,
/// Preferred store for MCP OAuth credentials.
/// keyring: Use an OS-specific keyring service.
/// Credentials stored in the keyring will only be readable by Codex unless the user explicitly grants access via OS-level keyring access.
/// https://github.com/openai/codex/blob/main/codex-rs/rmcp-client/src/oauth.rs#L2
/// file: CODEX_HOME/.credentials.json
/// This file will be readable to Codex and other applications running as the same user.
/// auto (default): keyring if available, otherwise file.
pub mcp_oauth_credentials_store_mode: OAuthCredentialsStoreMode,
/// Optional fixed port to use for the local HTTP callback server used during MCP OAuth login.
///
/// When unset, Codex will bind to an ephemeral port chosen by the OS.
pub mcp_oauth_callback_port: Option<u16>,
/// Optional redirect URI to use during MCP OAuth login.
///
/// When set, this URI is used in the OAuth authorization request instead
/// of the local listener address. The local callback listener still binds
/// to 127.0.0.1 (using `mcp_oauth_callback_port` when provided).
pub mcp_oauth_callback_url: Option<String>,
/// Combined provider map (defaults plus user-defined providers).
pub model_providers: HashMap<String, ModelProviderInfo>,
/// Maximum number of bytes to include from an AGENTS.md project doc file.
pub project_doc_max_bytes: usize,
/// Additional filenames to try when looking for project-level docs.
pub project_doc_fallback_filenames: Vec<String>,
/// Token budget applied when storing tool/function outputs in the context manager.
pub tool_output_token_limit: Option<usize>,
/// Maximum number of agent threads that can be open concurrently.
pub agent_max_threads: Option<usize>,
/// Maximum runtime in seconds for agent job workers before they are failed.
pub agent_job_max_runtime_seconds: Option<u64>,
/// Whether to record a model-visible message when an agent turn is interrupted.
pub agent_interrupt_message_enabled: bool,
/// Maximum nesting depth allowed for spawned agent threads.
pub agent_max_depth: i32,
/// User-defined role declarations keyed by role name.
pub agent_roles: BTreeMap<String, AgentRoleConfig>,
/// Memories subsystem settings.
pub memories: MemoriesConfig,
/// Directory containing all Codex state (defaults to `~/.codex` but can be
/// overridden by the `CODEX_HOME` environment variable).
pub codex_home: AbsolutePathBuf,
/// Directory where Codex stores the SQLite state DB.
pub sqlite_home: PathBuf,
/// Directory where Codex writes log files (defaults to `$CODEX_HOME/log`).
pub log_dir: PathBuf,
/// Directory where Codex writes effective session config lock files.
pub config_lock_export_dir: Option<AbsolutePathBuf>,
/// Whether config lock replay ignores Codex version drift between the
/// lock metadata and the regenerated lock.
pub config_lock_allow_codex_version_mismatch: bool,
/// Whether config lock creation saves values resolved from the model
/// catalog/session configuration.
pub config_lock_save_fields_resolved_from_model_catalog: bool,
/// Effective config lock used for strict replay validation.
pub config_lock_toml: Option<Arc<ConfigLockfileToml>>,
/// Settings that govern if and what will be written to `~/.codex/history.jsonl`.
pub history: History,
/// When true, session is not persisted on disk. Default to `false`
pub ephemeral: bool,
/// Whether enabled hooks should run without requiring persisted hook trust for this session.
///
/// This is a runtime-only knob populated from invocation overrides, not from config files.
pub bypass_hook_trust: bool,
/// Optional URI-based file opener. If set, citations to files in the model
/// output will be hyperlinked using the specified URI scheme.
pub file_opener: UriBasedFileOpener,
/// Path to the current Codex executable. This cannot be set in the config
/// file: it must be set in code via [`ConfigOverrides`].
pub codex_self_exe: Option<PathBuf>,
/// Path to the `codex-linux-sandbox` executable. This must be set if
/// [`codex_sandboxing::SandboxType::LinuxSeccomp`] is used. Note that this
/// cannot be set in the config file: it must be set in code via
/// [`ConfigOverrides`].
///
/// When this program is invoked, arg0 will be set to `codex-linux-sandbox`.
pub codex_linux_sandbox_exe: Option<PathBuf>,
/// Path to the `codex-execve-wrapper` executable used for shell
/// escalation. This cannot be set in the config file: it must be set in
/// code via [`ConfigOverrides`].
pub main_execve_wrapper_exe: Option<PathBuf>,
/// Optional absolute path to patched zsh used by zsh-exec-bridge-backed shell execution.
pub zsh_path: Option<PathBuf>,
/// Value to use for `reasoning.effort` when making a request using the
/// Responses API.
pub model_reasoning_effort: Option<ReasoningEffort>,
/// Optional Plan-mode-specific reasoning effort override used by the TUI.
///
/// When unset, Plan mode uses the built-in Plan preset default (currently
/// `medium`). When explicitly set (including `none`), this overrides the
/// Plan preset. The `none` value means "no reasoning" (not "inherit the
/// global default").
pub plan_mode_reasoning_effort: Option<ReasoningEffort>,
/// Optional value to use for `reasoning.summary` when making a request
/// using the Responses API. When unset, the model catalog default is used.
pub model_reasoning_summary: Option<ReasoningSummary>,
/// Optional override to force-enable reasoning summaries for the configured model.
pub model_supports_reasoning_summaries: Option<bool>,
/// Optional full model catalog loaded from `model_catalog_json`.
/// When set, this replaces the bundled catalog for the current process.
pub model_catalog: Option<ModelsResponse>,
/// Optional verbosity control for GPT-5 models (Responses API `text.verbosity`).
pub model_verbosity: Option<Verbosity>,
/// Base URL for requests to ChatGPT (as opposed to the OpenAI API).
pub chatgpt_base_url: String,
/// Optional path override for the host-owned apps MCP server.
pub apps_mcp_path_override: Option<String>,
/// Optional product SKU forwarded to the host-owned apps MCP server.
pub apps_mcp_product_sku: Option<String>,
/// Machine-local realtime audio device preferences used by realtime voice.
pub realtime_audio: RealtimeAudioConfig,
/// Experimental / do not use. Overrides only the realtime conversation
/// websocket transport base URL (the `Op::RealtimeConversation`
/// `/v1/realtime`
/// connection) without changing normal provider HTTP requests.
pub experimental_realtime_ws_base_url: Option<String>,
/// Experimental / do not use. Selects the realtime websocket model/snapshot
/// used for the `Op::RealtimeConversation` connection.
pub experimental_realtime_ws_model: Option<String>,
/// Experimental / do not use. Realtime websocket session selection.
/// `version` controls v1/v2 and `type` controls conversational/transcription.
pub realtime: RealtimeConfig,
/// Experimental / do not use. Overrides only the realtime conversation
/// websocket transport instructions (the `Op::RealtimeConversation`
/// `/ws` session.update instructions) without changing normal prompts.
pub experimental_realtime_ws_backend_prompt: Option<String>,
/// Experimental / do not use. Replaces the synthesized realtime startup
/// context appended to websocket session instructions. An empty string
/// disables startup context injection entirely.
pub experimental_realtime_ws_startup_context: Option<String>,
/// Experimental / do not use. Replaces the built-in realtime start
/// instructions inserted into developer messages when realtime becomes
/// active.
pub experimental_realtime_start_instructions: Option<String>,
/// Experimental / do not use. When set, app-server fetches thread-scoped
/// config from a remote service at this endpoint.
pub experimental_thread_config_endpoint: Option<String>,
/// Experimental / do not use. Selects the thread persistence backend.
pub experimental_thread_store: ThreadStoreConfig,
/// When set, restricts ChatGPT login to one or more workspace identifiers.
pub forced_chatgpt_workspace_id: Option<Vec<String>>,
/// When set, restricts the login mechanism users may use.
pub forced_login_method: Option<ForcedLoginMethod>,
/// Explicit or feature-derived web search mode.
pub web_search_mode: Constrained<WebSearchMode>,
/// Additional parameters for the web search tool when it is enabled.
pub web_search_config: Option<WebSearchConfig>,
/// If set to `true`, used only the experimental unified exec tool.
pub use_experimental_unified_exec_tool: bool,
/// Maximum poll window for background terminal output (`write_stdin`), in milliseconds.
/// Default: `300000` (5 minutes).
pub background_terminal_max_timeout: u64,
/// Compatibility-only settings retained for legacy `ghost_snapshot`
/// config loading.
pub ghost_snapshot: GhostSnapshotConfig,
/// Settings specific to the task-path-based multi-agent tool surface.
pub multi_agent_v2: MultiAgentV2Config,
/// Centralized feature flags; source of truth for feature gating.
pub features: ManagedFeatures,
/// When `true`, suppress warnings about unstable (under development) features.
pub suppress_unstable_features_warning: bool,
/// The active profile name used to derive this `Config` (if any).
pub active_profile: Option<String>,
/// The currently active project config, resolved by checking if cwd:
/// is (1) part of a git repo, (2) a git worktree, or (3) just using the cwd
pub active_project: ProjectConfig,
/// Collection of various notices we show the user
pub notices: Notice,
/// When `true`, checks for Codex updates on startup and surfaces update prompts.
/// Set to `false` only if your Codex updates are centrally managed.
/// Defaults to `true`.
pub check_for_update_on_startup: bool,
/// When true, disables burst-paste detection for typed input entirely.
/// All characters are inserted as they are received, and no buffering
/// or placeholder replacement will occur for fast keypress bursts.
pub disable_paste_burst: bool,
/// When `false`, disables analytics across Codex product surfaces in this machine.
/// Voluntarily left as Optional because the default value might depend on the client.
pub analytics_enabled: Option<bool>,
/// When `false`, disables feedback collection across Codex product surfaces.
/// Defaults to `true`.
pub feedback_enabled: bool,
/// Configured discoverable tools for tool suggestions.
pub tool_suggest: ToolSuggestConfig,
/// OTEL configuration (exporter type, endpoint, headers, etc.).
pub otel: codex_config::types::OtelConfig,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub struct MultiAgentV2Config {
pub max_concurrent_threads_per_session: usize,
pub min_wait_timeout_ms: i64,
pub max_wait_timeout_ms: i64,
pub default_wait_timeout_ms: i64,
pub usage_hint_enabled: bool,
pub usage_hint_text: Option<String>,
pub root_agent_usage_hint_text: Option<String>,
pub subagent_usage_hint_text: Option<String>,
pub tool_namespace: Option<String>,
pub hide_spawn_agent_metadata: bool,
pub non_code_mode_only: bool,
}
impl Default for MultiAgentV2Config {
fn default() -> Self {
Self {
max_concurrent_threads_per_session:
DEFAULT_MULTI_AGENT_V2_MAX_CONCURRENT_THREADS_PER_SESSION,
min_wait_timeout_ms: DEFAULT_MULTI_AGENT_V2_MIN_WAIT_TIMEOUT_MS,
max_wait_timeout_ms: DEFAULT_MULTI_AGENT_V2_MAX_WAIT_TIMEOUT_MS,
default_wait_timeout_ms: DEFAULT_MULTI_AGENT_V2_DEFAULT_WAIT_TIMEOUT_MS,
usage_hint_enabled: true,
usage_hint_text: None,
root_agent_usage_hint_text: None,
subagent_usage_hint_text: None,
tool_namespace: None,
hide_spawn_agent_metadata: false,
non_code_mode_only: false,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum TerminalResizeReflowMaxRows {
/// Use the runtime terminal detector to choose a scrollback-sized cap.
#[default]
Auto,