chopratejas--headroom
0ef5fcb1c5
Security / Dependency audit (pip-audit) (push) Has been cancelled
Security / CodeQL (javascript-typescript) (push) Has been cancelled
Security / CodeQL (python) (push) Has been cancelled
Security / Secret scan (gitleaks) (push) Has been cancelled
rust / test (ubuntu) (push) Has been cancelled
rust / simulator e2e (macos-latest) (push) Has been cancelled
rust / simulator e2e (ubuntu-latest) (push) Has been cancelled
rust / simulator e2e (windows-latest) (push) Has been cancelled
rust / wheels (aarch64-apple-darwin) (push) Has been cancelled
rust / wheels (x86_64-unknown-linux-gnu) (push) Has been cancelled
rust / wheels (x86_64-apple-darwin) (push) Has been cancelled
rust / audit (push) Has been cancelled
rust / parity (nightly, allowed to fail during Phase 0) (push) Has been cancelled
CI / commitlint (push) Has been skipped
Dev Containers / validate (.devcontainer/devcontainer.json, default) (push) Failing after 0s
Dev Containers / validate (.devcontainer/memory-stack/devcontainer.json, memory-stack) (push) Failing after 0s
Dev Containers / validate-worktree (push) Failing after 0s
CI / changes (push) Failing after 4s
Deploy Documentation / validate (push) Has been skipped
Deploy Documentation / deploy (push) Failing after 1s
Init Native E2E / init-native (ubuntu-latest, claude) (push) Failing after 1s
Init Native E2E / init-native (ubuntu-latest, codex) (push) Failing after 1s
Install Native E2E / install-native (ubuntu-latest) (push) Failing after 1s
OpenCode Plugin / typecheck + build + test (push) Failing after 1s
Init Native E2E / init-native (ubuntu-latest, copilot) (push) Failing after 1s
Release Please / release-please (push) Failing after 1s
Wrap E2E / docker-wrap-e2e (push) Failing after 1s
Wrap Native E2E / wrap-native (ubuntu-latest) (push) Failing after 1s
Init E2E / docker-init-e2e (push) Failing after 4s
Merge Conflicts / merge-conflicts (push) Failing after 4s
CI / lint (push) Has been cancelled
CI / build-wheel (push) Has been cancelled
CI / build-wheel-windows (push) Has been cancelled
CI / prefetch-model (push) Has been cancelled
CI / test-dashboard-ui (push) Has been cancelled
CI / test (1) (push) Has been cancelled
CI / test (2) (push) Has been cancelled
CI / test (3) (push) Has been cancelled
CI / test (4) (push) Has been cancelled
CI / test-extras (push) Has been cancelled
CI / test-agno (push) Has been cancelled
CI / build (push) Has been cancelled
CI / workflow-validation (push) Has been cancelled
CI / docker-native-e2e (push) Has been cancelled
CI / windows-native-wrapper (push) Has been cancelled
CI / macos-native-wrapper (push) Has been cancelled
Docker / docker-build (map[name:arm64 platform:linux/arm64 runs_on:ubuntu-24.04-arm], map[bake_target:runtime-code-nonroot name:code-nonroot]) (push) Has been cancelled
Docker / docker-build (map[name:arm64 platform:linux/arm64 runs_on:ubuntu-24.04-arm], map[bake_target:runtime-code-slim name:code-slim]) (push) Has been cancelled
Docker / docker-build (map[name:arm64 platform:linux/arm64 runs_on:ubuntu-24.04-arm], map[bake_target:runtime-code-slim-nonroot name:code-slim-nonroot]) (push) Has been cancelled
Docker / docker-build (map[name:arm64 platform:linux/arm64 runs_on:ubuntu-24.04-arm], map[bake_target:runtime-nonroot name:nonroot]) (push) Has been cancelled
Docker / docker-build (map[name:arm64 platform:linux/arm64 runs_on:ubuntu-24.04-arm], map[bake_target:runtime-slim name:slim]) (push) Has been cancelled
Docker / docker-build (map[name:arm64 platform:linux/arm64 runs_on:ubuntu-24.04-arm], map[bake_target:runtime-slim-nonroot name:slim-nonroot]) (push) Has been cancelled
Docker / docker-manifest (map[bake_target:runtime name:]) (push) Has been cancelled
Docker / docker-manifest (map[bake_target:runtime-code name:code]) (push) Has been cancelled
Docker / docker-manifest (map[bake_target:runtime-code-nonroot name:code-nonroot]) (push) Has been cancelled
Docker / docker-manifest (map[bake_target:runtime-code-slim name:code-slim]) (push) Has been cancelled
Docker / docker-manifest (map[bake_target:runtime-code-slim-nonroot name:code-slim-nonroot]) (push) Has been cancelled
Docker / docker-manifest (map[bake_target:runtime-nonroot name:nonroot]) (push) Has been cancelled
Docker / docker-manifest (map[bake_target:runtime-slim name:slim]) (push) Has been cancelled
Docker / docker-manifest (map[bake_target:runtime-slim-nonroot name:slim-nonroot]) (push) Has been cancelled
Docker / docker-build (map[name:amd64 platform:linux/amd64 runs_on:ubuntu-24.04], map[bake_target:runtime name:]) (push) Has been cancelled
Docker / docker-build (map[name:amd64 platform:linux/amd64 runs_on:ubuntu-24.04], map[bake_target:runtime-code name:code]) (push) Has been cancelled
Docker / docker-build (map[name:amd64 platform:linux/amd64 runs_on:ubuntu-24.04], map[bake_target:runtime-code-nonroot name:code-nonroot]) (push) Has been cancelled
Docker / docker-build (map[name:amd64 platform:linux/amd64 runs_on:ubuntu-24.04], map[bake_target:runtime-code-slim name:code-slim]) (push) Has been cancelled
Docker / docker-build (map[name:amd64 platform:linux/amd64 runs_on:ubuntu-24.04], map[bake_target:runtime-code-slim-nonroot name:code-slim-nonroot]) (push) Has been cancelled
Docker / docker-build (map[name:amd64 platform:linux/amd64 runs_on:ubuntu-24.04], map[bake_target:runtime-nonroot name:nonroot]) (push) Has been cancelled
Docker / docker-build (map[name:amd64 platform:linux/amd64 runs_on:ubuntu-24.04], map[bake_target:runtime-slim name:slim]) (push) Has been cancelled
Docker / docker-build (map[name:amd64 platform:linux/amd64 runs_on:ubuntu-24.04], map[bake_target:runtime-slim-nonroot name:slim-nonroot]) (push) Has been cancelled
Docker / docker-build (map[name:arm64 platform:linux/arm64 runs_on:ubuntu-24.04-arm], map[bake_target:runtime name:]) (push) Has been cancelled
Docker / docker-build (map[name:arm64 platform:linux/arm64 runs_on:ubuntu-24.04-arm], map[bake_target:runtime-code name:code]) (push) Has been cancelled
Docker / promote-latest (push) Has been cancelled
Init Native E2E / init-native (macos-latest, claude) (push) Has been cancelled
Init Native E2E / init-native (macos-latest, codex) (push) Has been cancelled
Init Native E2E / init-native (macos-latest, copilot) (push) Has been cancelled
Install Native E2E / install-native (macos-latest) (push) Has been cancelled
Wrap Native E2E / wrap-native (macos-latest) (push) Has been cancelled
116 行
4.4 KiB
Python
116 行
4.4 KiB
Python
"""Reconcile Codex thread provider tags across the Headroom proxy boundary.
|
|
|
|
Codex stamps every thread with the ``model_provider`` it ran under and filters
|
|
its history/projects menu by the active provider set. When Headroom rewrites
|
|
Codex's config to route through the custom ``headroom`` provider (see
|
|
:mod:`headroom.providers.codex.install`), threads created through Headroom are
|
|
tagged ``headroom`` while native threads keep ``openai`` -- so the two sets never
|
|
appear in the same menu, and connecting Headroom appears to "lose" history.
|
|
|
|
To keep the menu whole we retag threads to match whichever provider is active:
|
|
``openai -> headroom`` when Headroom is enabled, ``headroom -> openai`` when it is
|
|
reverted. Only rows whose ``model_provider`` equals the source value are
|
|
touched, so third-party providers are left alone.
|
|
|
|
Every operation is best-effort: a missing store, a missing ``threads`` table, or
|
|
a store momentarily locked by a running Codex is logged and skipped -- never
|
|
raised -- so install/uninstall never fail on account of the history menu. The
|
|
store is WAL-mode, so the update succeeds even while Codex is running; the short
|
|
busy timeout only covers a transient checkpoint lock.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import logging
|
|
import re
|
|
import sqlite3
|
|
from pathlib import Path
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
HEADROOM_PROVIDER = "headroom"
|
|
NATIVE_PROVIDER = "openai"
|
|
|
|
# Seconds to wait on a busy store before giving up (a running Codex only holds an
|
|
# exclusive lock briefly, during a WAL checkpoint).
|
|
_BUSY_TIMEOUT_S = 0.75
|
|
_STATE_DB_RE = re.compile(r"^state_(\d+)\.sqlite$")
|
|
|
|
|
|
def _codex_state_db_paths(codex_home: Path) -> list[Path]:
|
|
"""Discover direct Codex state stores under the known home locations."""
|
|
discovered: list[Path] = []
|
|
seen: set[Path] = set()
|
|
for base in (codex_home / "sqlite", codex_home):
|
|
if not base.exists():
|
|
continue
|
|
matches: list[tuple[int, Path]] = []
|
|
try:
|
|
entries = list(base.iterdir())
|
|
except OSError:
|
|
continue
|
|
for path in entries:
|
|
match = _STATE_DB_RE.match(path.name)
|
|
if match is None or not path.is_file():
|
|
continue
|
|
matches.append((int(match.group(1)), path))
|
|
matches.sort(key=lambda item: (item[0], str(item[1])))
|
|
for _, path in matches:
|
|
resolved = path.resolve()
|
|
if resolved in seen:
|
|
continue
|
|
seen.add(resolved)
|
|
discovered.append(path)
|
|
return discovered
|
|
|
|
|
|
def _retag_one(path: Path, *, frm: str, to: str) -> int:
|
|
"""Retag a single store and return the number of rows moved.
|
|
|
|
No-ops (returns 0) on a store whose schema lacks the ``threads`` table.
|
|
"""
|
|
conn = sqlite3.connect(str(path), timeout=_BUSY_TIMEOUT_S)
|
|
try:
|
|
has_table = conn.execute(
|
|
"SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'threads'"
|
|
).fetchone()
|
|
if has_table is None:
|
|
return 0
|
|
cur = conn.execute(
|
|
"UPDATE threads SET model_provider = ? WHERE model_provider = ?",
|
|
(to, frm),
|
|
)
|
|
conn.commit()
|
|
return cur.rowcount
|
|
finally:
|
|
conn.close()
|
|
|
|
|
|
def retag_thread_providers(codex_home: Path, *, frm: str, to: str) -> None:
|
|
"""Best-effort retag of Codex thread provider tags across all known stores.
|
|
|
|
``codex_home`` is the Codex configuration directory (the parent of
|
|
``config.toml``); resolving from it keeps callers and tests pointed at one
|
|
location rather than re-deriving ``~/.codex`` independently.
|
|
"""
|
|
if frm == to:
|
|
return
|
|
for path in _codex_state_db_paths(codex_home):
|
|
try:
|
|
moved = _retag_one(path, frm=frm, to=to)
|
|
except (OSError, sqlite3.Error) as exc:
|
|
logger.warning("codex thread retag %s->%s skipped for %s: %s", frm, to, path, exc)
|
|
continue
|
|
if moved:
|
|
logger.info("codex thread retag %s->%s: %d thread(s) in %s", frm, to, moved, path)
|
|
|
|
|
|
def retag_to_headroom(codex_home: Path) -> None:
|
|
"""Pull existing native threads into the headroom-provider menu (on enable)."""
|
|
retag_thread_providers(codex_home, frm=NATIVE_PROVIDER, to=HEADROOM_PROVIDER)
|
|
|
|
|
|
def retag_to_native(codex_home: Path) -> None:
|
|
"""Hand threads back to the native-provider menu (on revert)."""
|
|
retag_thread_providers(codex_home, frm=HEADROOM_PROVIDER, to=NATIVE_PROVIDER)
|