Commit Graph

4780 Commits

  • chore: stop app-server auth refresh storms after permanent token failure (#15530)
    built from #14256. PR description from @etraut-openai:
    
    This PR addresses a hole in [PR
    11802](https://github.com/openai/codex/pull/11802). The previous PR
    assumed that app server clients would respond to token refresh failures
    by presenting the user with an error ("you must log in again") and then
    not making further attempts to call network endpoints using the expired
    token. While they do present the user with this error, they don't
    prevent further attempts to call network endpoints and can repeatedly
    call `getAuthStatus(refreshToken=true)` resulting in many failed calls
    to the token refresh endpoint.
    
    There are three solutions I considered here:
    1. Change the getAuthStatus app server call to return a null auth if the
    caller specified "refreshToken" on input and the refresh attempt fails.
    This will cause clients to immediately log out the user and return them
    to the log in screen. This is a really bad user experience. It's also a
    breaking change in the app server contract that could break third-party
    clients.
    2. Augment the getAuthStatus app server call to return an additional
    field that indicates the state of "token could not be refreshed". This
    is a non-breaking change to the app server API, but it requires
    non-trivial changes for all clients to properly handle this new field
    properly.
    3. Change the getAuthStatus implementation to handle the case where a
    token refresh fails by marking the AuthManager's in-memory access and
    refresh tokens as "poisoned" so it they are no longer used. This is the
    simplest fix that requires no client changes.
    
    I chose option 3.
    
    Here's Codex's explanation of this change:
    
    When an app-server client asks `getAuthStatus(refreshToken=true)`, we
    may try to refresh a stale ChatGPT access token. If that refresh fails
    permanently (for example `refresh_token_reused`, expired, or revoked),
    the old behavior was bad in two ways:
    
    1. We kept the in-memory auth snapshot alive as if it were still usable.
    2. Later auth checks could retry refresh again and again, creating a
    storm of doomed `/oauth/token` requests and repeatedly surfacing the
    same failure.
    
    This is especially painful for app-server clients because they poll auth
    status and can keep driving the refresh path without any real chance of
    recovery.
    
    This change makes permanent refresh failures terminal for the current
    managed auth snapshot without changing the app-server API contract.
    
    What changed:
    - `AuthManager` now poisons the current managed auth snapshot in memory
    after a permanent refresh failure, keyed to the unchanged `AuthDotJson`.
    - Once poisoned, later refresh attempts for that same snapshot fail fast
    locally without calling the auth service again.
    - The poison is cleared automatically when auth materially changes, such
    as a new login, logout, or reload of different auth state from storage.
    - `getAuthStatus(includeToken=true)` now omits `authToken` after a
    permanent refresh failure instead of handing out the stale cached bearer
    token.
    
    This keeps the current auth method visible to clients, avoids forcing an
    immediate logout flow, and stops repeated refresh attempts for
    credentials that cannot recover.
    
    ---------
    
    Co-authored-by: Eric Traut <etraut@openai.com>
  • chore: use access token expiration for proactive auth refresh (#15545)
    Follow up to #15357 by making proactive ChatGPT auth refresh depend on
    the access token's JWT expiration instead of treating `last_refresh` age
    as the primary source of truth.
  • feat: communication pattern v2 (#15647)
    See internal communication
  • app-server: Add back pressure and batching to command/exec (#15547)
    * Add
    `OutgoingMessageSender::send_server_notification_to_connection_and_wait`
    which returns only once message is written to websocket (or failed to do
    so)
    * Use this mechanism to apply back pressure to stdout/stderr streams of
    processes spawned by `command/exec`, to limit them to at most one
    message in-memory at a time
    * Use back pressure signal to also batch smaller chunks into ≈64KiB ones
    
    This should make commands execution more robust over
    high-latency/low-throughput networks
  • core: Make FileWatcher reusable (#15093)
    ### Summary
    Make `FileWatcher` a reusable core component which can be built upon.
    Extract skills-related logic into a separate `SkillWatcher`.
    Introduce a composable `ThrottledWatchReceiver` to throttle filesystem
    events, coalescing affected paths among them.
    
    ### Testing
    Updated existing unit tests.
  • Increase voice space hold timeout to 1s (#15579)
    Increase the space-hold delay to 1 second before voice capture starts,
    and mirror the change in tui_app_server.
  • Pretty plugin labels, preserve plugin app provenance during MCP tool refresh (#15606)
    - Prefer plugin manifest `interface.displayName` for plugin labels.
    - Preserve plugin provenance when handling `list_mcp_tools` so connector
    `plugin_display_names` are not clobbered.
    - Add a TUI test to ensure plugin-owned app mentions are deduped
    correctly.
  • try to fix git glitch (#15650)
    Empty commit on branch 	t git glitch debugging.
  • try to fix git glitch (#15651)
    Empty commit on branch 	t git glitch debugging.
  • try to fix git glitch (#15652)
    Empty commit on branch 	t git glitch debugging.
  • try to fix git glitch (#15653)
    Empty commit on branch 	t git glitch debugging.
  • try to fix git glitch (#15654)
    Empty commit on branch 	t git glitch debugging.
  • try to fix git glitch (#15655)
    Empty commit on branch 	t git glitch debugging.
  • try to fix git glitch (#15656)
    Empty commit on branch 	t git glitch debugging.
  • try to fix git glitch (#15657)
    Empty commit on branch 	t git glitch debugging.
  • try to fix git glitch (#15658)
    Empty commit on branch 	t git glitch debugging.
  • [codex] Stabilize second compaction history test (#15605)
    ## Summary
    - replace the second-compaction test fixtures with a single ordered
    `/responses` sequence
    - assert against the real recorded request order instead of aggregating
    per-mock captures
    - realign the second-summary assertion to the first post-compaction user
    turn where the summary actually appears
    
    ## Root cause
    `compact_resume_after_second_compaction_preserves_history` collected
    requests from multiple `mount_sse_once_match` recorders. Overlapping
    matchers could record the same HTTP request more than once, so the test
    indexed into a duplicated synthetic list rather than the true request
    stream. That made the summary assertion depend on matcher evaluation
    order and platform-specific behavior.
    
    ## Impact
    - makes the flaky test deterministic by removing duplicate request
    capture from the assertion path
    - keeps the change scoped to the test only
    
    ## Validation
    - `just fmt`
    - `just argument-comment-lint`
    - `env -u CODEX_SANDBOX_NETWORK_DISABLED cargo test -p codex-core
    compact_resume_after_second_compaction_preserves_history -- --nocapture`
    - repeated the same targeted test 10 times
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • feat: disable notifier v2 and start turn on agent interaction (#15624)
    Make the inter-agent communication start a turn
    
    As part of this, we disable the v2 notifier to prevent some odd
    behaviour where the agent restart working while you're talking to it for
    example
  • Refresh mentions list after plugin install/uninstall (#15598)
    Refresh mentions list after plugin install/uninstall to that $mentions
    are updated without requiring exiting/launching the client.
  • [Codex TUI] - Sort /plugins TUI menu by installed status first, alpha second (#15558)
    Updates plugin ordering so installed plugins are listed first, with
    alphabetical sorting applied within the installed and uninstalled
    groups. The behavior is now consistent across both `tui` and
    `tui_app_server`, and related tests/snapshots were updated.
  • Bump vedantmgoyal9/winget-releaser from 19e706d4c9121098010096f9c495a70a7518b30f to 7bd472be23763def6e16bd06cc8b1cdfab0e2fd5 (#14777)
    Bumps
    [vedantmgoyal9/winget-releaser](https://github.com/vedantmgoyal9/winget-releaser)
    from 19e706d4c9121098010096f9c495a70a7518b30f to
    7bd472be23763def6e16bd06cc8b1cdfab0e2fd5.
    <details>
    <summary>Commits</summary>
    <ul>
    <li><a
    href="https://github.com/vedantmgoyal9/winget-releaser/commit/7bd472be23763def6e16bd06cc8b1cdfab0e2fd5"><code>7bd472b</code></a>
    docs: add description to inputs (<a
    href="https://redirect.github.com/vedantmgoyal9/winget-releaser/issues/335">#335</a>)</li>
    <li><a
    href="https://github.com/vedantmgoyal9/winget-releaser/commit/a43926ed822e5a076ac8a81e0a794915cbad51d1"><code>a43926e</code></a>
    fix: cargo command not found in <code>ubuntu-slim</code> runner (<a
    href="https://redirect.github.com/vedantmgoyal9/winget-releaser/issues/334">#334</a>)</li>
    <li>See full diff in <a
    href="https://github.com/vedantmgoyal9/winget-releaser/compare/19e706d4c9121098010096f9c495a70a7518b30f...7bd472be23763def6e16bd06cc8b1cdfab0e2fd5">compare
    view</a></li>
    </ul>
    </details>
    <br />
    
    
    Dependabot will resolve any conflicts with this PR as long as you don't
    alter it yourself. You can also trigger a rebase manually by commenting
    `@dependabot rebase`.
    
    [//]: # (dependabot-automerge-start)
    [//]: # (dependabot-automerge-end)
    
    ---
    
    <details>
    <summary>Dependabot commands and options</summary>
    <br />
    
    You can trigger Dependabot actions by commenting on this PR:
    - `@dependabot rebase` will rebase this PR
    - `@dependabot recreate` will recreate this PR, overwriting any edits
    that have been made to it
    - `@dependabot show <dependency name> ignore conditions` will show all
    of the ignore conditions of the specified dependency
    - `@dependabot ignore this major version` will close this PR and stop
    Dependabot creating any more for this major version (unless you reopen
    the PR or upgrade to it yourself)
    - `@dependabot ignore this minor version` will close this PR and stop
    Dependabot creating any more for this minor version (unless you reopen
    the PR or upgrade to it yourself)
    - `@dependabot ignore this dependency` will close this PR and stop
    Dependabot creating any more for this dependency (unless you reopen the
    PR or upgrade to it yourself)
    
    
    </details>
    
    Signed-off-by: dependabot[bot] <support@github.com>
    Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
  • chore(deps): bump pnpm/action-setup from 4 to 5 (#15484)
    Bumps [pnpm/action-setup](https://github.com/pnpm/action-setup) from 4
    to 5.
    <details>
    <summary>Release notes</summary>
    <p><em>Sourced from <a
    href="https://github.com/pnpm/action-setup/releases">pnpm/action-setup's
    releases</a>.</em></p>
    <blockquote>
    <h2>v5.0.0</h2>
    <p>Updated the action to use Node.js 24.</p>
    <h2>v4.4.0</h2>
    <p>Updated the action to use Node.js 24.</p>
    <h2>v4.3.0</h2>
    <h2>What's Changed</h2>
    <ul>
    <li>docs: fix the run_install example in the Readme by <a
    href="https://github.com/dreyks"><code>@​dreyks</code></a> in <a
    href="https://redirect.github.com/pnpm/action-setup/pull/175">pnpm/action-setup#175</a></li>
    <li>chore: remove unused <code>@types/node-fetch</code> dependency by <a
    href="https://github.com/silverwind"><code>@​silverwind</code></a> in <a
    href="https://redirect.github.com/pnpm/action-setup/pull/186">pnpm/action-setup#186</a></li>
    <li>Clarify that package_json_file is relative to GITHUB_WORKSPACE by <a
    href="https://github.com/chris-martin"><code>@​chris-martin</code></a>
    in <a
    href="https://redirect.github.com/pnpm/action-setup/pull/184">pnpm/action-setup#184</a></li>
    <li>feat: store caching by <a
    href="https://github.com/jrmajor"><code>@​jrmajor</code></a> in <a
    href="https://redirect.github.com/pnpm/action-setup/pull/188">pnpm/action-setup#188</a></li>
    <li>refactor: remove star imports by <a
    href="https://github.com/KSXGitHub"><code>@​KSXGitHub</code></a> in <a
    href="https://redirect.github.com/pnpm/action-setup/pull/196">pnpm/action-setup#196</a></li>
    <li>fix(ci): exclude macos by <a
    href="https://github.com/KSXGitHub"><code>@​KSXGitHub</code></a> in <a
    href="https://redirect.github.com/pnpm/action-setup/pull/197">pnpm/action-setup#197</a></li>
    </ul>
    <h2>New Contributors</h2>
    <ul>
    <li><a href="https://github.com/dreyks"><code>@​dreyks</code></a> made
    their first contribution in <a
    href="https://redirect.github.com/pnpm/action-setup/pull/175">pnpm/action-setup#175</a></li>
    <li><a
    href="https://github.com/silverwind"><code>@​silverwind</code></a> made
    their first contribution in <a
    href="https://redirect.github.com/pnpm/action-setup/pull/186">pnpm/action-setup#186</a></li>
    <li><a
    href="https://github.com/chris-martin"><code>@​chris-martin</code></a>
    made their first contribution in <a
    href="https://redirect.github.com/pnpm/action-setup/pull/184">pnpm/action-setup#184</a></li>
    <li><a href="https://github.com/jrmajor"><code>@​jrmajor</code></a> made
    their first contribution in <a
    href="https://redirect.github.com/pnpm/action-setup/pull/188">pnpm/action-setup#188</a></li>
    <li><a
    href="https://github.com/Boosted-Bonobo"><code>@​Boosted-Bonobo</code></a>
    made their first contribution in <a
    href="https://redirect.github.com/pnpm/action-setup/pull/199">pnpm/action-setup#199</a></li>
    </ul>
    <p><strong>Full Changelog</strong>: <a
    href="https://github.com/pnpm/action-setup/compare/v4.2.0...v4.3.0">https://github.com/pnpm/action-setup/compare/v4.2.0...v4.3.0</a></p>
    <h2>v4.2.0</h2>
    <p>When there's a <code>.npmrc</code> file at the root of the
    repository, pnpm will be fetched from the registry that is specified in
    that <code>.npmrc</code> file <a
    href="https://redirect.github.com/pnpm/action-setup/pull/179">#179</a></p>
    <h2>v4.1.0</h2>
    <p>Add support for <code>package.yaml</code> <a
    href="https://redirect.github.com/pnpm/action-setup/pull/156">#156</a>.</p>
    </blockquote>
    </details>
    <details>
    <summary>Commits</summary>
    <ul>
    <li>See full diff in <a
    href="https://github.com/pnpm/action-setup/compare/v4...v5">compare
    view</a></li>
    </ul>
    </details>
    <br />
    
    
    [![Dependabot compatibility
    score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=pnpm/action-setup&package-manager=github_actions&previous-version=4&new-version=5)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
    
    Dependabot will resolve any conflicts with this PR as long as you don't
    alter it yourself. You can also trigger a rebase manually by commenting
    `@dependabot rebase`.
    
    [//]: # (dependabot-automerge-start)
    [//]: # (dependabot-automerge-end)
    
    ---
    
    <details>
    <summary>Dependabot commands and options</summary>
    <br />
    
    You can trigger Dependabot actions by commenting on this PR:
    - `@dependabot rebase` will rebase this PR
    - `@dependabot recreate` will recreate this PR, overwriting any edits
    that have been made to it
    - `@dependabot show <dependency name> ignore conditions` will show all
    of the ignore conditions of the specified dependency
    - `@dependabot ignore this major version` will close this PR and stop
    Dependabot creating any more for this major version (unless you reopen
    the PR or upgrade to it yourself)
    - `@dependabot ignore this minor version` will close this PR and stop
    Dependabot creating any more for this minor version (unless you reopen
    the PR or upgrade to it yourself)
    - `@dependabot ignore this dependency` will close this PR and stop
    Dependabot creating any more for this dependency (unless you reopen the
    PR or upgrade to it yourself)
    
    
    </details>
    
    Signed-off-by: dependabot[bot] <support@github.com>
    Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
  • Finish moving codex exec to app-server (#15424)
    This PR completes the conversion of non-interactive `codex exec` to use
    app server rather than directly using core events and methods.
    
    ### Summary
    - move `codex-exec` off exec-owned `AuthManager` and `ThreadManager`
    state
    - route exec bootstrap, resume, and auth refresh through existing
    app-server paths
    - replace legacy `codex/event/*` decoding in exec with typed app-server
    notification handling
    - update human and JSONL exec output adapters to translate existing
    app-server notifications only
    - clean up "app server client" layer by eliminating support for legacy
    notifications; this is no longer needed
    - remove exposure of `authManager` and `threadManager` from "app server
    client" layer
    
    ### Testing
    - `exec` has pretty extensive unit and integration tests already, and
    these all pass
    - In addition, I asked Codex to put together a comprehensive manual set
    of tests to cover all of the `codex exec` functionality (including
    command-line options), and it successfully generated and ran these tests
  • Allow global network allowlist wildcard (#15549)
    ## Problem
    
    Today `codex-network-proxy` rejects a global `*` in
    `network.allowed_domains`, so there is no static way to configure a
    denylist-only posture for public hosts. Users have to enumerate broad
    allowlist patterns instead.
    
    ## Approach
    
    - Make global wildcard acceptance field-specific: `allowed_domains` can
    use `*`, while `denied_domains` still rejects a global wildcard.
    - Keep the existing evaluation order, so explicit denies still win first
    and local/private protections still apply unless separately enabled.
    - Add coverage for the denylist-only behavior and update the README to
    document it.
    
    ## Validation
    
    - `just fmt`
    - `cargo test -p codex-network-proxy` (full run had one unrelated flaky
    telemetry test:
    `network_policy::tests::emit_block_decision_audit_event_emits_non_domain_event`;
    reran in isolation and it passed)
    - `cargo test -p codex-network-proxy
    network_policy::tests::emit_block_decision_audit_event_emits_non_domain_event
    -- --exact --nocapture`
    - `just fix -p codex-network-proxy`
    - `just argument-comment-lint`
  • feat: list agents for sub-agent v2 (#15621)
    Add a `list_agents` for multi-agent v2, optionally path based
    
    This return the task and status of each agent in the matched path
  • Stabilize macOS CI test timeouts (#15581)
    ## Summary
    - raise the shell snapshot apply_patch helper timeout to avoid macOS CI
    startup races
    - increase the shared MCP app-server test read timeout so slow
    initialize handshakes do not fail command_exec tests spuriously
    
    ## Testing
    - cargo test -p codex-core
    shell_command_snapshot_still_intercepts_apply_patch
    - cargo test -p codex-app-server
    command_exec_tty_implies_streaming_and_reports_pty_output
    
    Co-authored-by: Codex <noreply@openai.com>
  • Remove filter from plugins/list result (#15580)
    Show all plugin marketplaces in the /plugins popup by removing the
    `openai-curated` marketplace filter, and update plugin popup
    copy/tests/snapshots to match the new behavior in both TUI codepaths.
  • Move sandbox policy transforms into codex-sandboxing (#15599)
    ## Summary
    - move the pure sandbox policy transform helpers from `codex-core` into
    `codex-sandboxing`
    - move the corresponding unit tests with the extracted implementation
    - update `core` and `app-server` callers to import the moved APIs
    directly, without re-exports or proxy methods
    
    ## Testing
    - cargo test -p codex-sandboxing
    - cargo test -p codex-core sandboxing
    - cargo test -p codex-app-server --lib
    - just fix -p codex-sandboxing
    - just fix -p codex-core
    - just fix -p codex-app-server
    - just fmt
    - just argument-comment-lint
  • move imagegen skill into system skills (#15600)
    Add imagegen skill as built-in skill. Source: github.com/openai/skills
  • [codex-cli][app-server] Update self-serve business usage limit copy in error returned (#15478)
    ## Summary
    - update the self-serve business usage-based limit message to direct
    users to their admin for additional credits
    - add a focused unit test for the self_serve_business_usage_based plan
    branch
    
    Added also: 
    
    If you are at a rate limit but you still have credits, codex cli would
    tell you to switch the model. We shouldnt do this if you have credits so
    fixed this.
    
    ## Test
    - launched the source-built CLI and verified the updated message is
    shown for the self-serve business usage-based plan
    
    ![Test
    screenshot](https://raw.githubusercontent.com/openai/codex/5cc3c013ef17ac5c66dfd9395c0d3c4837602231/docs/images/self-serve-business-usage-limit.png)
  • Move macOS sandbox builders into codex-sandboxing (#15593)
    ## Summary
    - move macOS permission merging/intersection logic and tests from
    `codex-core` into `codex-sandboxing`
    - move seatbelt policy builders, permissions logic, SBPL assets, and
    their tests into `codex-sandboxing`
    - keep `codex-core` owning only the seatbelt spawn wrapper and switch
    call sites to import the moved APIs directly
    
    ## Notes
    - no re-exports added
    - moved the seatbelt tests with the implementation so internal helpers
    could stay private
    - local verification is still finishing while this PR is open
  • Extract landlock helpers into codex-sandboxing (#15592)
    ## Summary
    - add a new `codex-sandboxing` crate for sandboxing extraction work
    - move the pure Linux sandbox argv builders and their unit tests out of
    `codex-core`
    - keep `core::landlock` as the spawn wrapper and update direct callers
    to use `codex_sandboxing::landlock`
    
    ## Testing
    - `cargo test -p codex-sandboxing`
    - `cargo test -p codex-core landlock`
    - `cargo test -p codex-cli debug_sandbox`
    - `just argument-comment-lint`
    
    ## Notes
    - this is step 1 of the move plan aimed at minimizing per-PR diffs
    - no re-exports or no-op proxy methods were added
  • Add fork snapshot modes (#15239)
    ## Summary
    - add `ForkSnapshotMode` to `ThreadManager::fork_thread` so callers can
    request either a committed snapshot or an interrupted snapshot
    - share the model-visible `<turn_aborted>` history marker between the
    live interrupt path and interrupted forks
    - update the small set of direct fork callsites to pass
    `ForkSnapshotMode::Committed`
    
    Note: this enables /btw to work similarly as Esc to interrupt (hopefully
    somewhat in distribution)
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • feat: custom watcher for multi-agent v2 (#15576)
    The new wait tool just returns `Wait timed out.` or `Wait completed.`.
    The actual content is done through the notification watcher
  • feat: custom watcher for multi-agent v2 (#15570)
    Custom watcher that sends an InterAgentCommunication on end of turn
  • [codex] Add rollback context duplication snapshot (#15562)
    ## What changed
    - adds a targeted snapshot test for rollback with contextual diffs in
    `codex_tests.rs`
    - snapshots the exact model-visible request input before the rolled-back
    turn and on the follow-up request after rollback
    - shows the duplicate developer and environment context pair appearing
    again before the follow-up user message
    
    ## Why
    Rollback currently rewinds the reference context baseline without
    rewinding the live session overrides. On the next turn, the same
    contextual diff is emitted again and duplicated in the request sent to
    the model.
    
    ## Impact
    - makes the regression visible in a canonical snapshot test
    - keeps the snapshot on the shared `context_snapshot` path without
    adding new formatting helpers
    - gives a direct repro for future fixes to rollback/context
    reconstruction
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • chore(core) Add approvals reviewer to UserTurn (#15426)
    ## Summary
    Adds support for approvals_reviewer to `Op::UserTurn` so we can migrate
    `[CodexMessageProcessor::turn_start]` to use Op::UserTurn
    
    ## Testing
    - [x] Adds quick test for the new field
    
    Co-authored-by: Codex <noreply@openai.com>
  • feat: use serde to differenciate inter agent communication (#15560)
    Use `serde` to encode the inter agent communication to an assistant
    message and use the decode to see if this is such a message
    
    Note: this assume serde on small pattern is fast enough
  • [hooks] add non-streaming (non-stdin style) shell-only PreToolUse support (#15211)
    - add `PreToolUse` hook for bash-like tool execution only at first
    - block shell execution before dispatch with deny-only hook behavior
    - introduces common.rs matcher framework for matching when hooks are run
    
    example run:
    
    ```
    › run three parallel echo commands, and the second one should echo "[block-pre-tool-use]" as a test
    
    
    • Running the three echo commands in parallel now and I’ll report the output directly.
    
    • Running PreToolUse hook: name for demo pre tool use hook
    
    • Running PreToolUse hook: name for demo pre tool use hook
    
    • Running PreToolUse hook: name for demo pre tool use hook
    
    PreToolUse hook (completed)
      warning: wizard-tower PreToolUse demo inspected Bash: echo "first parallel echo"
      
    PreToolUse hook (blocked)
      warning: wizard-tower PreToolUse demo blocked a Bash command on purpose.
      feedback: PreToolUse demo blocked the command. Remove [block-pre-tool-use] to continue.
    
    PreToolUse hook (completed)
      warning: wizard-tower PreToolUse demo inspected Bash: echo "third parallel echo"
    
    • Ran echo "first parallel echo"
      └ first parallel echo
    
    • Ran echo "third parallel echo"
      └ third parallel echo
    
    • Three little waves went out in parallel.
    
      1. printed first parallel echo
      2. was blocked before execution because it contained the exact test string [block-pre-tool-use]
      3. printed third parallel echo
    
      There was also an unrelated macOS defaults warning around the successful commands, but the echoes
      themselves worked fine. If you want, I can rerun the second one with a slightly modified string so
      it passes cleanly.
    ```
  • feat: new op type for sub-agents communication (#15556)
    Add `InterAgentCommunication` for v2 agent communication
  • Unify realtime stop handling in TUI (#15529)
    ## Summary
    - route /realtime, Ctrl+C, and deleted realtime meters through the same
    realtime stop path
    - keep generic transcription placeholder cleanup free of realtime
    shutdown side effects
    
    ## Testing
    - Ran 
    - Relied on CI for verification; did not run local tests
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>