Skip to content

feat(proxy): subagent prompt-cache TTL and session lifecycle - #1309

Closed
tobwen wants to merge 3 commits into
Soju06:mainfrom
tobwen:feat/subagent-prompt-cache-ttl
Closed

feat(proxy): subagent prompt-cache TTL and session lifecycle#1309
tobwen wants to merge 3 commits into
Soju06:mainfrom
tobwen:feat/subagent-prompt-cache-ttl

Conversation

@tobwen

@tobwen tobwen commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds a subagent prompt-cache affinity TTL setting (default NULL / No Cache) that controls how long subagent bridge sessions retain their stream lease and PROMPT_CACHE sticky mapping after the response stream ends. Also closes internal_unanchored_parallel fork bridge sessions immediately after stream completion, preventing them from holding stream leases for the parent's 3600s idle TTL.

Type of change

  • fix: - bug fix (no behavior change beyond the bug)
  • feat: - new user-facing feature or capability
  • refactor: - internal refactor (no behavior change, no API change)
  • docs: - documentation only
  • chore: / ci: / build: - tooling, CI, packaging
  • test: - test-only change
  • Breaking change

Linked issue: Closes #1307

OpenSpec

  • This PR includes / updates an OpenSpec change
  • Not applicable - bug fix that matches the existing spec
  • Not applicable - docs / CI / chore only
  • This PR touches a codex-faithful path (image pipeline, request/response shape, SSE framing, OAuth flow) and preserves upstream-equivalent behavior

Change directory: openspec/changes/add-subagent-prompt-cache-ttl/

Changes

Subagent detection and TTL

  • Detect subagent sessions via x-parent-session-id, x-openai-subagent, or x-codex-parent-thread-id headers in _get_or_create_http_bridge_session
  • Add http_responses_session_bridge_subagent_prompt_cache_ttl_seconds dashboard setting (nullable Integer, default NULL)
  • NULL or 0: suppress sticky key and mapping for subagents; bridge session and stream lease released immediately after stream end
  • Positive value: bridge session, stream lease, and PROMPT_CACHE mapping retained for the configured TTL, then released via delayed background close task

Fork close

  • internal_unanchored_parallel fork sessions are closed immediately when their response stream ends, releasing the stream lease
  • Parent session's bridge and sticky mapping remain available

Sticky session persistence

  • Add is_subagent column to sticky_sessions table (Alembic migration)
  • is_subagent flag propagates from header detection through account selection to sticky session persistence
  • Cleanup scheduler purges subagent mappings using the subagent TTL, independently from parent mappings
  • Scheduler interval capped at 30 seconds for prompt subagent TTL cleanup

Dashboard

  • Routing Settings control labelled "Subagent prompt-cache affinity TTL" (empty = No Cache)
  • Sticky sessions table displays "Prompt cache, Subagent" for subagent entries

Alternatives considered

  • Fork-based is_fork flag with a separate fork idle TTL. Discarded because OpenCode sends tool calls as ordinary conversation requests, not as distinguishable forks. The is_fork flag could not reliably identify the target sessions.
  • x-openai-subagent header as the sole detection signal. Discarded because OpenCode does not send this header. x-parent-session-id is the actual header OpenCode sends for subagent requests.
  • Active bridge sessions API (GET/DELETE /api/http-bridge-sessions) with a dashboard table. Discarded to keep scope minimal. The fork close and subagent TTL already release stream leases promptly.

Test plan

# uv run pytest tests/unit/test_proxy_http_bridge.py::test_fork_session_closed_after_stream_end -xvs
# uv run pytest tests/unit/test_select_with_stickiness.py -q
# uv run pytest tests/unit/test_sticky_session_cleanup_scheduler.py -q
# uv run pytest tests/integration/test_sticky_sessions_api.py -q
# uv run ruff check && uv run ruff format --check
# uv run openspec validate --specs

330 passed, ruff clean, OpenSpec 42/42.

Screenshots / output (optional)

image image

Checklist

  • Title is in Conventional Commits format (<type>(<scope>)?: <subject>).
  • Linked the related issue / discussion above.
  • Added or updated tests covering the change.
  • Ran uv run ruff check and uv run ruff format --check on changed files.
  • If touching specs: openspec validate --specs passes and /opsx:verify is clean.
  • CHANGELOG is not edited by hand (release-please handles it).

@github-actions github-actions Bot added the db migration PR changes Alembic database migrations; maintainer must coordinate merge order label Jul 14, 2026
@tobwen
tobwen force-pushed the feat/subagent-prompt-cache-ttl branch 11 times, most recently from c18c81c to beb1807 Compare July 14, 2026 20:07
@Soju06

Soju06 commented Jul 17, 2026

Copy link
Copy Markdown
Owner

Maintainer decision: direction approved — header-based subagent detection (x-parent-session-id and friends) with a nullable prompt-cache TTL setting fits how we already do session affinity, and the immediate close of internal_unanchored_parallel fork sessions at stream end addresses a real leak. To get this to merge-ready:

  1. Rebase onto current main (branch is conflicting).
  2. Re-parent the migration: 20260713_080000_add_subagent_prompt_cache_ttl currently revises 20260713_020000_add_model_registry_snapshot, which already has a child on main — merging as-is creates a multi-head graph. Re-parent onto the current head (20260717_000000_optimize_dashboard_hot_path_indexes as of today; run alembic heads after rebase to confirm).
  3. Trigger @codex review after the push — there's no Codex evidence on this PR yet, and clean-on-head is a merge gate.
  4. One hardening ask from my read: guard the delayed subagent-close task against session reuse — if the same bridge session is re-attached between stream end and the deferred close, the close should be skipped rather than tearing down a live session.

Ping me once it's rebased and reviewed and I'll take it from there.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: beb18073f8

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread app/db/alembic/versions/20260713_080000_add_subagent_prompt_cache_ttl.py Outdated
Comment thread app/modules/proxy/_service/http_bridge/streaming.py Outdated
Comment thread app/modules/sticky_sessions/cleanup_scheduler.py
@Komzpa Komzpa added the 🤖 codex: needs work [@codex review] raised an issue label Jul 17, 2026
@Komzpa
Komzpa force-pushed the feat/subagent-prompt-cache-ttl branch from beb1807 to 80e994b Compare July 18, 2026 06:23
@Komzpa Komzpa removed the 🤖 codex: needs work [@codex review] raised an issue label Jul 18, 2026
@Komzpa

Komzpa commented Jul 18, 2026

Copy link
Copy Markdown
Collaborator

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 80e994bea7

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread app/modules/proxy/_service/http_bridge/mixin.py Outdated
Comment thread app/modules/proxy/sticky_repository.py
Comment thread app/modules/proxy/_service/http_bridge/mixin.py
Comment thread app/modules/sticky_sessions/service.py
Comment thread frontend/src/features/settings/components/routing-settings.tsx
@Komzpa Komzpa added 🤖 codex: needs work [@codex review] raised an issue and removed 🤖 codex: needs work [@codex review] raised an issue labels Jul 18, 2026
@Komzpa

Komzpa commented Jul 18, 2026

Copy link
Copy Markdown
Collaborator

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 21bad215db

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread app/modules/proxy/_service/http_bridge/helpers.py Outdated
Comment thread app/modules/sticky_sessions/service.py
Comment thread app/modules/proxy/_service/http_bridge/helpers.py Outdated
Comment thread app/modules/sticky_sessions/service.py
Comment thread app/modules/settings/api.py
Comment thread app/modules/proxy/_service/http_bridge/helpers.py
@Komzpa Komzpa added the 🤖 codex: needs work [@codex review] raised an issue label Jul 18, 2026
@Komzpa
Komzpa force-pushed the feat/subagent-prompt-cache-ttl branch 2 times, most recently from f55ca31 to ce237e4 Compare July 18, 2026 07:57
@Komzpa

Komzpa commented Jul 18, 2026

Copy link
Copy Markdown
Collaborator

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ce237e416d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread app/modules/proxy/_service/http_bridge/mixin.py
@Komzpa
Komzpa force-pushed the feat/subagent-prompt-cache-ttl branch from ce237e4 to a2b3716 Compare July 18, 2026 12:06
@Komzpa
Komzpa force-pushed the feat/subagent-prompt-cache-ttl branch 2 times, most recently from ee12b05 to 4f9a321 Compare July 18, 2026 12:25
@Komzpa Komzpa removed the 🤖 codex: needs work [@codex review] raised an issue label Jul 18, 2026
@Komzpa
Komzpa force-pushed the feat/subagent-prompt-cache-ttl branch from 4f9a321 to de1d932 Compare July 18, 2026 12:40
tobwen and others added 2 commits July 18, 2026 16:43
Add subagent prompt-cache affinity TTL setting with NULL default (No
Cache). Subagent sessions are detected via x-parent-session-id header.

- NULL/0 TTL: bridge session and stream lease released immediately
  after stream end; no sticky mapping created
- Positive TTL: bridge session retained for configured duration, then
  closed; sticky mapping created and cleaned after TTL

Unanchored parallel forks (internal_unanchored_parallel) are closed
immediately after stream end, releasing their stream lease. This
prevents parallel fork sessions from consuming stream capacity for
the parent prompt-cache idle TTL (3600s).

The is_subagent flag propagates from header detection through account
selection to sticky session persistence. The cleanup scheduler respects
subagent TTL independently from parent sessions.

Includes dashboard settings control, frontend display, Alembic
migrations, and OpenSpec specifications.
@Komzpa
Komzpa force-pushed the feat/subagent-prompt-cache-ttl branch from de1d932 to e148598 Compare July 18, 2026 12:45
@Komzpa
Komzpa force-pushed the feat/subagent-prompt-cache-ttl branch from e148598 to 4cce15b Compare July 18, 2026 12:55
@Komzpa Komzpa added the needs rebase Needs rebase or conflict repair against current main label Jul 22, 2026
@Soju06

Soju06 commented Jul 24, 2026

Copy link
Copy Markdown
Owner

Still want this for #1307 — the header-based subagent detection and immediate close of internal_unanchored_parallel fork sessions were already approved in direction (see the maintainer note above). The branch is now CONFLICTING against current main (head 4cce15b). Please rebase onto current main; note this PR carries the db migration label, so keep the Alembic revision chained off main's current head when you rebase. Merge gates will run right after.

@Soju06

Soju06 commented Jul 30, 2026

Copy link
Copy Markdown
Owner

Timing note from triage: this is feature-class, so per the release policy it's queued behind the v1.23.0 stable cut (#1472) regardless of rebase state — no need to chase the current conflicts yet, since main will move again when the cut lands. Once #1472 merges, please rebase onto that main (heads-up: the conflict surface today spans load_balancer.py, sticky_repository.py, the http_bridge mixin/streaming split, and scripts/check_proxy_architecture.py, so expect a real rework rather than a mechanical one; the migration will also need re-parenting onto the current alembic head) and we'll run the review gates then. The header-based subagent detection direction from the earlier review still stands approved.

@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

This pull request has been automatically marked as stale because it has had no activity for 7 days.

It will be closed in 23 more days unless there is new activity.

If this is still relevant, please:

  • Rebase or push an update if the branch drifted
  • Address pending review feedback if there is any
  • Leave a short comment confirming it is still being worked on

Thanks for the contribution 🙏

@github-actions github-actions Bot added the stale No response from reporter; scheduled for close label Aug 7, 2026
@Soju06

Soju06 commented Aug 12, 2026

Copy link
Copy Markdown
Owner

De-staling: this is intentionally queued behind the stable release train (now v1.23.1, #1692/#1693) per the feature-freeze note above — the inactivity is on our side, not the author's. No need to chase the conflicts until the train lands; I'll ping for a rebase when the queue opens.

@Komzpa

Komzpa commented Aug 14, 2026

Copy link
Copy Markdown
Collaborator

Status note: this overlaps the newer thread-identity affinity work in #1703, which carries the same problem family on a narrower current design. Holding this open until #1703 resolves; maintainer preference for the marker/TTL/schema design here would revive it.

@Soju06

Soju06 commented Aug 16, 2026

Copy link
Copy Markdown
Owner

Post-#1703 status: the thread-identity affinity work has now merged (35bbb00), and it deliberately moves away from subagent-marker inference — the x-parent-session-id-family detection, the is_subagent sticky column, and the migration in this PR are built on the design #1703 replaced. #1747's idle-session sweep also now reclaims stream leases from idle bridge sessions, covering most of the original #1307 leak. What this PR still uniquely adds is (a) the configurable subagent prompt-cache affinity TTL setting and (b) deterministic close of internal_unanchored_parallel fork sessions at stream end rather than sweep-based reclaim. Maintainer call needed: if #1703 + #1747 are considered sufficient for #1307, this PR (and the issue) can be closed; if the TTL control is still wanted, it should be redesigned on top of #1703's (process session, thread-id) identity rather than rebased — the current branch would be a ground-up rework either way.

@Soju06

Soju06 commented Aug 16, 2026

Copy link
Copy Markdown
Owner

Closing after maintainer review: the detection mechanism here (x-parent-session-id / x-openai-subagent header inference + an is_subagent sticky-sessions column) is now structurally superseded — #1703 (merged 08-15) derives a versioned internal thread key from Codex's own (process session, thread-id) identity contract and explicitly rejects marker-based subagent inference, and #1747's idle-session sweep addresses the original lease-hogging complaint from #1307. A rebase would be a ground-up redesign rather than a mechanical update, as predicted back on 07-30. What this PR still uniquely proposed — a configurable subagent cache-affinity TTL and deterministic close of fork sessions at stream end (vs sweep-based reclaim) — remains a valid feature direction, but it would now be keyed on #1703's thread identity. #1307 stays open to track that; if the sweep turns out to be sufficient in practice we'll close it too. Thanks for the design groundwork — the TTL/lifecycle framing shaped how we evaluated #1703.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

db migration PR changes Alembic database migrations; maintainer must coordinate merge order needs rebase Needs rebase or conflict repair against current main

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: subagent prompt-cache affinity TTL

3 participants