12 Commits

  • Clarify model-generated and legacy app path types (#28577)
    ## Why
    
    `ApiPathString` kind of implies that it can be used anywhere we pull a
    path out of JSON, but it's not really appropriate for tool arguments
    when the model might generate relative paths.
    
    Prefer `String` for model-generated paths and we can handle the
    conversion per feature for now and define a shared abstraction later if
    it makes sense.
    
    # What
    
    Rename `ApiPathString` to `AppLegacyPathString` to clarify its role.
    
    Expand the `path-types` skill to tell the model to leave tool args as
    bare strings.
  • Use ApiPathString in app-server filesystem permission paths (#28367)
    ## Why
    
    Clients running an app-server on one OS and an exec-server on another OS
    need to be able to pass sandbox config to app-server that refers to
    resources on the executor's foreign OS.
    
    ## What
    
    `AbsolutePathBuf` can't represent these paths and we don't want users to
    be exposed to `PathUri` yet, so this moves the public app-server API to
    be expressed in terms of `ApiPathString`.
    
    Stacked on #28165.
    
    - change app-server v2 filesystem permission paths, including legacy
    read/write roots, to `ApiPathString`
    - localize API paths through `PathUri` when converting into the current
    native core permission types
    - make path-bearing permission conversions fallible and surface
    localization failures instead of silently treating malformed grants as
    ordinary denials
    - propagate conversion failures through app-server and TUI approval
    handling
    - regenerate the app-server JSON and TypeScript schemas
    - leave migration TODOs on native-path conversions so they can be
    removed once core permission paths use `PathUri`
  • Make deny canonical for filesystem permission entries (#23493)
    ## Why
    Filesystem permission profiles used `none` for deny-read entries, which
    is less direct than the action the entry actually represents. This
    change makes `deny` the canonical filesystem permission spelling while
    preserving compatibility for older configs that still send `none`.
    
    ## What changed
    - rename `FileSystemAccessMode::None` to `Deny`
    - serialize and generate schemas with `deny` as the canonical value
    - retain `none` only as a legacy input alias for temporary config
    compatibility
    - update filesystem glob diagnostics and regression coverage to use the
    canonical spelling
    - refresh config and app-server schema fixtures to match the new wire
    shape
    
    ## Validation
    - `cargo test -p codex-protocol`
    - `cargo test -p codex-app-server-protocol`
    - `cargo test -p codex-core config_toml_deserializes_permission_profiles
    --lib`
    - `cargo test -p codex-core
    read_write_glob_patterns_still_reject_non_subpath_globs --lib`
    
    Earlier in the session, a broad `cargo test -p codex-core` run reached
    unrelated pre-existing failures in timing/snapshot/git-info tests under
    this environment; the targeted surfaces touched by this PR passed
    cleanly.
  • permissions: remove cwd special path (#19841)
    ## Why
    
    The experimental `PermissionProfile` API had both `:cwd` and
    `:project_roots` special filesystem paths, which made the permission
    root ambiguous. This PR removes the unstable `current_working_directory`
    special path before the permissions API is stabilized, so callers use
    `:project_roots` for symbolic project-root access.
    
    ## What changed
    
    - Removes `FileSystemSpecialPath::CurrentWorkingDirectory` from protocol
    and app-server protocol models, plus regenerated app-server
    JSON/TypeScript schemas.
    - Replaces internal `:cwd` permission entries with `:project_roots`
    entries.
    - Keeps the existing cwd-update behavior for legacy-shaped
    workspace-write profiles, while removing the deleted
    `CurrentWorkingDirectory` case from that compatibility path.
    - Keeps `PermissionProfile::workspace_write()` as the reusable symbolic
    workspace-write helper, with docs noting that `:project_roots` entries
    resolve at enforcement time.
    - Updates app-server docs/examples and approval UI labeling to stop
    advertising `:cwd` as a permission token.
    
    ## Compatibility
    
    Persisted rollout items may contain the old
    `{"kind":"current_working_directory"}` tag from earlier experimental
    `permissionProfile` snapshots. This PR keeps that tag as a
    deserialize-only alias for `ProjectRoots { subpath: None }`, while
    continuing to serialize only the new `project_roots` tag.
    
    ## Follow-up
    
    This PR intentionally does not introduce an explicit project-root set on
    `SessionConfiguration` or runtime sandbox resolution. Today, the
    resolver still uses the active cwd as the single implicit project root.
    A follow-up should model project roots separately from tool cwd so
    `:project_roots` entries can resolve against the configured project
    roots, and resolve to no entries when there are no project roots.
    
    ## Verification
    
    - `cargo test -p codex-protocol permissions:: --lib`
    - `cargo test -p codex-app-server-protocol`
    - `cargo test -p codex-sandboxing -p codex-exec-server --lib`
    - `cargo test -p codex-core session_configuration_apply_ --lib`
    - `cargo test -p codex-app-server
    command_exec_permission_profile_project_roots_use_command_cwd --test
    all`
    - `cargo test -p codex-tui
    thread_read_session_state_does_not_reuse_primary_permission_profile
    --lib`
    - `cargo test -p codex-tui
    preset_matching_accepts_workspace_write_with_extra_roots --lib`
    - `cargo test -p codex-config --lib`
  • app-server: include filesystem entries in permission requests (#19086)
    ## Why
    
    `item/permissions/requestApproval` sends a requested permission profile
    to app-server clients. The core profile already stores filesystem
    permissions as `entries`, but the v2 compatibility conversion used the
    legacy `read`/`write` projection whenever possible and left `entries`
    unset.
    
    That made the request ambiguous for clients that consume the canonical
    v2 shape: `permissions.fileSystem.entries` was missing even though
    filesystem access was being requested. A client that rendered or echoed
    grants from `entries` could treat the request as having no filesystem
    permission entries, then return an empty or incomplete grant. The
    app-server intersects responses with the original request, so omitted
    filesystem permissions are denied.
    
    ## What Changed
    
    - Populate `AdditionalFileSystemPermissions.entries` when converting
    legacy read/write roots for request permission payloads, while
    preserving `read` and `write` for compatibility.
    - Mark `read` and `write` as transitional schema fields in the generated
    app-server schema.
    - Add regression coverage for the v2 conversion, the app-server
    `item/permissions/requestApproval` round trip, and TUI app-server
    approval conversion expectations.
    - Refresh generated JSON and TypeScript schema fixtures.
    
    ## Verification
    
    - `just fmt`
    - `cargo test -p codex-app-server-protocol`
    - `cargo test -p codex-app-server request_permissions_round_trip`
    - `cargo test -p codex-tui
    converts_request_permissions_into_granted_permissions`
    - `cargo test -p codex-tui
    resolves_permissions_and_user_input_through_app_server_request_id`
  • feat(request-permissions) approve with strict review (#19050)
    ## Summary
    Allow the user to approve a request_permissions_tool request with the
    condition that all commands in the rest of the turn are reviewed by
    guardian, regardless of sandbox status.
    
    ## Testing
    - [x] Added unit tests
    - [x] Ran locally
  • protocol: preserve glob scan depth in permission profiles (#18713)
    ## Why
    
    #18274 made `PermissionProfile` the canonical file-system permissions
    shape, but the round-trip from `FileSystemSandboxPolicy` to
    `PermissionProfile` still dropped one piece of policy metadata:
    `glob_scan_max_depth`.
    
    That field is security-relevant for deny-read globs such as `**/*.env`.
    On Linux, bubblewrap sandbox construction uses it to bound unreadable
    glob expansion. If a profile copied from active runtime permissions
    loses this value and is submitted back as an override, the resulting
    `FileSystemSandboxPolicy` can behave differently even though the visible
    permission entries look equivalent.
    
    ## What changed
    
    - Add `glob_scan_max_depth` to protocol `FileSystemPermissions` and
    preserve it when converting to/from `FileSystemSandboxPolicy`.
    - Keep legacy `read`/`write` JSON for simple path-only permissions, but
    force canonical JSON when glob scan depth is present so the metadata is
    not silently dropped.
    - Carry `globScanMaxDepth` through app-server
    `AdditionalFileSystemPermissions`, generated JSON/TypeScript schemas,
    and app-server/TUI conversion call sites.
    - Preserve the metadata through sandboxing permission normalization,
    merging, and intersection.
    - Carry the merged scan depth into the effective
    `FileSystemSandboxPolicy` used for command execution, so bounded
    deny-read globs reach Linux bubblewrap materialization.
    
    ## Verification
    
    - `cargo test -p codex-sandboxing glob_scan -- --nocapture`
    - `cargo test -p codex-sandboxing policy_transforms -- --nocapture`
    - `just fix -p codex-sandboxing`
    
    
    
    
    
    ---
    [//]: # (BEGIN SAPLING FOOTER)
    Stack created with [Sapling](https://sapling-scm.com). Best reviewed
    with [ReviewStack](https://reviewstack.dev/openai/codex/pull/18713).
    * #18288
    * #18287
    * #18286
    * #18285
    * #18284
    * #18283
    * #18282
    * #18281
    * #18280
    * #18279
    * #18278
    * #18277
    * #18276
    * #18275
    * __->__ #18713
  • protocol: canonicalize file system permissions (#18274)
    ## Why
    
    `PermissionProfile` needs stable, canonical file-system semantics before
    it can become the primary runtime permissions abstraction. Without a
    canonical form, callers have to keep re-deriving legacy sandbox maps and
    profile comparisons remain lossy or order-dependent.
    
    ## What changed
    
    This adds canonicalization helpers for `FileSystemPermissions` and
    `PermissionProfile`, expands special paths into explicit sandbox
    entries, and updates permission request/conversion paths to consume
    those canonical entries. It also tightens the legacy bridge so root-wide
    write profiles with narrower carveouts are not silently projected as
    full-disk legacy access.
    
    ## Verification
    
    - `cargo test -p codex-protocol
    root_write_with_read_only_child_is_not_full_disk_write -- --nocapture`
    - `cargo test -p codex-sandboxing permission -- --nocapture`
    - `cargo test -p codex-tui permissions -- --nocapture`
  • feat: Add additional macOS Sandbox Permissions for Launch Services, Contacts, Reminders (#14155)
    Add additional macOS Sandbox Permissions levers for the following:
    
    - Launch Services
    - Contacts
    - Reminders
  • feat(core) Persist request_permission data across turns (#14009)
    ## Summary
    request_permissions flows should support persisting results for the
    session.
    
    Open Question: Still deciding if we need within-turn approvals - this
    adds complexity but I could see it being useful
    
    ## Testing
    - [x] Updated unit tests
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • Add request permissions tool (#13092)
    Adds a built-in `request_permissions` tool and wires it through the
    Codex core, protocol, and app-server layers so a running turn can ask
    the client for additional permissions instead of relying on a static
    session policy.
    
    The new flow emits a `RequestPermissions` event from core, tracks the
    pending request by call ID, forwards it through app-server v2 as an
    `item/permissions/requestApproval` request, and resumes the tool call
    once the client returns an approved subset of the requested permission
    profile.