Extension Version Pinning and Profile Conductor#34
Merged
Conversation
Add SHOULD_BUILD_REH and SHOULD_BUILD_REH_WEB exports to dev/build.sh to prevent "unbound variable" errors during local builds. Add .claude/ and fv to .gitignore.
Replace hardcoded extension download logic in get-extensions.sh with a declarative bundle-extensions.json config. Extensions are downloaded as pre-built VSIXs from GitHub Releases via `gh release download` and unpacked into vscode/extensions/ during the build.
A workbench contribution baked into the Codex shell that enforces project-scoped extension version pins. Reads pin declarations from project metadata.json (or Frontier's workspaceState), downloads VSIXs from GitHub Release URLs, installs them into deterministic VS Code profiles, and switches the extension host. Includes mid-session pin detection via IStorageService signals, a 3-cycle reload-loop circuit breaker, 14-day automatic profile cleanup, and a progress notification UX with "Reload Codex When Ready".
Adds `codex pin list/add/remove` subcommands to the Rust CLI for managing extension version pins in project metadata.json. The `add` command downloads a remote VSIX, extracts the extension ID and version from its package.json, and writes the pin entry. The patch registers `pin` as a native CLI command in argv.ts with Node-to-Rust hand-off, adds PinningError to the error types, and refactors the macOS "Install Shell Command" to create both a `codex` and `codex-cli` symlink (the latter pointing directly to codex-tunnel for direct Rust CLI access without the Node wrapper). pin.rs is delivered via source overlay; the patch modifies existing VS Code files (args, argv, nativeHostMainService). The patch is built on the baseline of binary-name.patch which it depends on.
Move development instructions to AGENTS.md (readable by both humans and AI agents). CLAUDE.md becomes a symlink to AGENTS.md for backward compatibility.
…ex components Remove redundant tutorial-style content and stale merge strategy details. Add build pipeline diagram, overlay vs patch guidance, patch dependency table, and documentation for CodexConductor, CLI pin commands, and extension bundling.
…hardcoded "code" The Rust CLI's version_manager.rs had five hardcoded references to "code" as the editor binary name. This caused codex-tunnel commands (e.g. pin) to fail with "No such file or directory" when looking for bin/code instead of bin/codex. ## Changes - Update DESKTOP_CLI_RELATIVE_PATH to use concatcp! with APPLICATION_NAME - Update detect_installed_program /Applications/ fast path - Update detect_installed_program system_profiler fallback path
NativeExtensionManagementService.downloadVsix() intercepts install() calls in the renderer and downloads via browser fetch(), which fails for GitHub release URLs due to CORS on the 302 redirect. Route the install call through the shared process IPC channel directly, where Node.js networking handles redirects without CORS restrictions. Also adds retry logic with backoff, profile cleanup on failure, and richer error reporting with Copy Error Report action.
…ement Resolved a reliability issue where extension version pin enforcement would enter an infinite reload loop, especially when running in extension development mode or when custom editors vetoed the extension host restart. Core Changes: - Implement "Authoritative Reload": Patched VS Code core to allow the reload() IPC command to accept an explicit forceProfile name. - Patch windowImpl.ts to respect the passed profile name and explicitly revive workspace URIs during lookup to bypass Main process stale-cache issues. - Patch windowsMainService.ts to allow profile-workspace associations to be persisted even when launched with --extensionDevelopmentPath. - Update CodexConductor to use the authoritative reload signal and explicitly call resetWorkspaces() before switching to prevent lookup conflicts. Build System: - Fix build_cli.sh to use mkdir -p when preparing OpenSSL to prevent spurious build failures. Documentation: - Updated AGENTS.md with details on the authoritative reload and robustness features.
The forceProfile authoritative reload path looks up the profile from Main process memory, not disk. The timeout was unnecessary since the profile association is already in-memory when reload fires.
Adds a Command Palette command (Codex: Manage Extension Pins) for in-editor pin management. Supports viewing required/pinned extensions, adding pins from VSIX URLs or GitHub release pages, removing pins, and syncing via Frontier. Also adds GitHub release page URL resolution to the Rust CLI pin command, and fixes metadata.json serialization to use 4-space indent matching codex-editor. ## Changes - New codexPinManager.ts workbench contribution with QuickPick hub UI - CLI resolve_vsix_url() resolves release page URLs to VSIX download URLs - CLI write_metadata uses 4-space indent (matches codex-editor convention)
…nvalid metadata Previously, calling initialize() multiple times leaked storage listeners, non-FOLDER workspaces left users stranded on conductor profiles, and missing or invalid metadata.json caused early returns that skipped revertIfPatchBuild().
…profiles The CONDUCTOR_PROFILE_PATTERN regex didn't match pre-release version suffixes (e.g. codex-editor-v0.24.0-pr816-1148908f), so revertIfPatchBuild() silently skipped revert when opening a project without pins. Setting the 'repo-pinned' icon on creation provides a reliable, self-describing marker on the profile itself.
After removing a pin, metadata.json retains an empty `pinnedExtensions: {}`. `readPinsSnapshot()` treated this as truthy, returning `"{}"` instead of `undefined`, causing `resolveProfileName()` to error on `undefined.includes('.')`.
`setProfileForWorkspace` internally updates `currentProfile` even when the extension host vetos the switch. The post-call ID check then incorrectly reports "already on target" and skips the authoritative reload, causing duplicate extension registrations and a blank sidebar. Capture the profile ID before the call instead.
… all boundaries Consolidates duplicate PinnedExtensionEntry, PinnedExtensions, RequiredExtensions, and ProjectMetadata declarations into a shared module. Adds parsePinnedExtensions() which validates entry shape (string version and url) and drops malformed entries, replacing all raw JSON.parse casts.
VS Code stores an extension's entire workspaceState as a single JSON blob under the extension ID key. The conductor was reading a dotted subkey (frontier-rnd.frontier-authentication.remotePinnedExtensions) that never existed. Read the blob key and extract the remotePinnedExtensions field from within it. This fixes the entire storage-based flow: initial enforcement from remote pins, mid-session detection, and sync deadlock resolution.
…entication Change RequiredExtensions from Record<string, string> to an interface with specific codexEditor and frontierAuthentication keys. Update pin manager hub to use known keys instead of generic Object.keys() indexing.
The codex-editor extension activates before frontier-authentication after conductor profile switches (onView vs onStartupFinished), so frontier commands aren't available yet. This command lets codex-editor read the resolved pins directly from IStorageService without depending on frontier.
Introduces a dedicated Service API for extension pinning, moving state ownership from Frontier into the Conductor. - Adds storage keys for adminPinnedExtensions, remotePinnedExtensions, and syncCompletedAt in the Conductor namespace. - Registers IPC commands (setAdminPinIntent, setRemotePins, getPinMismatches, etc.) to provide a clean API for other extensions. - Centralizes version mismatch detection using internal shell services for better reliability. - Updates codexPinManager UI to use the new Conductor-owned state commands.
Ensures reliable mid-session state synchronization and correct notification triggers. - Adds a storage listener for syncCompletedAt to trigger state re-evaluation after sync lands metadata on disk (fixes Scenario 3). - Explicitly calls storageService.remove() when pins are cleared to ensure listeners fire reliably. - Removes development-era storage key annotations and cleans up internal state handling.
Exposes a new `codex.conductor.hasAdminPinIntent` command that returns true when `adminPinnedExtensions` is set in workspace storage. Extensions (codex-editor) can query this to suppress auto-sync while the admin is sanity-testing a freshly pinned extension version.
Codex* was matching src/.../codexConductor on macOS's case-insensitive filesystem, silently ignoring the entire conductor source tree. Fixed by changing it to /Codex* (root-anchored). Also: - Remove redundant vscode/ (already covered by the explicit rule) - Add *.vscdb to ignore VS Code workspace state databases - Group rules logically with comments
… associations mid-session Three fixes: - readPinsSnapshot() now canonicalizes both top-level key order and nested entry field order, preventing false-positive pin change detection from JSON property ordering differences across parse/write cycles. - Mid-session reload paths (pin change, pin removal, install completion) now use switchProfileAndReload() instead of bare hostService.reload(), which persists the workspace-profile association via setProfileForWorkspace() before reloading. Previously, forceProfile handled the immediate reload but the association was never persisted, causing potential extra reload cycles on subsequent reopens. - The "Reload Codex When Ready" auto-reload path now awaits switchProfileAndReload() so rejections are caught by the surrounding try/catch and surfaced via the error notification UX.
…wline to metadata writes - Remove get_vsix_metadata_smart() which made real HEAD + Range requests on every `pin add` then unconditionally fell through to full download, wasting two round-trips. Replaced with a TODO documenting the intended optimization. - write_metadata() now appends a trailing newline to match the TypeScript PinManager's JSON output, avoiding noisy git diffs when both paths touch the same metadata.json.
If Codex quits mid-VSIX-install, the profile exists on disk but has no extensions. Previously, the conductor trusted name match as proof of completeness, causing a stuck state where codex-editor yields forever. Now calls getInstalled(type, profileLocation) to verify pinned extensions are present before skipping download. Incomplete profiles are repaired in-place.
Updates the pre-existing RequiredExtensions type definition to ensure cross-boundary compatibility and correct parsing between the codex shell and upstream repositories like codex-editor and frontier-authentication. This guarantees structural type safety across boundaries.
Modifies the existing Rust CLI build process and adds the base pin add, remove, and list subcommands. This provides a robust terminal interface for admins to locally manage pinnedExtensions programmatically. Includes necessary semver validation, trailing newline formatting for metadata writes, and sets up the codex-cli macOS symlink.
Extends the new CLI pinning module to add sync and reset commands. This is a distinct functional addition to the CLI that allows administrators to safely stage, commit, or discard their local pin changes using git-based dirty checks directly from the command line interface.
switchProfileAndReload called resetWorkspaces() as a pre-cleanup, but that method clears workspaces on every profile globally — so switching profiles in one project window erased other open projects' associations. The pre-cleanup was also redundant: setProfileForWorkspace calls updateProfile, which cascades and removes the workspace from every other profile automatically (common/userDataProfile.ts:376-381).
The payload handed to lifecycleMainService.reload() is a NativeParsedArgs
and every key we set ({ _, 'disable-extensions', 'profile' }) is already
declared on that interface (argv.ts:42, 93, 138). The cast was bypassing
type safety for no reason.
The authoritative-reload patch removed the !extensionDevelopmentPath guard around setProfileForWorkspace in doOpenInBrowserWindow, but the guard removal wasn't actually needed for the conductor flow: - Reloads route through windowImpl.ts (forceProfile by name lookup), not doOpenInBrowserWindow. - The conductor persists associations via the renderer IPC, which hits the unconditional main-process setProfileForWorkspace — no EDH gate there. - Subsequent EDH opens read the persisted association via getProfileForWorkspace (guard only blocks writes, not reads). Removing the hunk restores upstream behavior: dev launches with --profile scratch --extensionDevelopmentPath don't contaminate the workspace-profile association for later non-EDH opens.
e893df5 to
3590fd5
Compare
1 similar comment
- stable-linux: add GITHUB_TOKEN to compile Build step env so
get-extensions.sh's `gh release download` can authenticate.
- get-extensions.sh / build.sh: execute get-extensions.sh instead of
sourcing it, so its `set -u` doesn't leak into build.sh and trip
the CI_BUILD unbound-variable check on the Windows compile job.
- dev/build.sh: make SHOULD_BUILD_REH / SHOULD_BUILD_REH_WEB
overridable via env (${VAR:-no}) so the Dockerfile's inline
`SHOULD_BUILD_REH=yes SHOULD_BUILD_REH_WEB=yes ./dev/build.sh`
actually takes effect.
- Dockerfile: override SHOULD_BUILD_REH=yes SHOULD_BUILD_REH_WEB=yes
on `./dev/build.sh`, since the runtime stage needs
vscode-reh-web-linux-x64/ which dev/build.sh now skips by default.
- pr-build: pass --repo to gh pr comment in notify-failure so the
build-failed comment and thumbs-down reaction post without
requiring an actions/checkout in that job.
7ce7a52 to
4b48379
Compare
Author
|
/build |
- compile-windows: cross-compile on ubuntu-22.04 with OS_NAME=windows, upload vscode.tar.gz artifact. - build-windows: unpack on windows-2022, run package.sh, sign app binaries (.exe/.dll) and installers (.exe/.msi) in two passes with SSL.com eSigner so binaries inside the installer are also signed. - release: include windows-x64 .exe and .msi in the PR prerelease, add build-windows to release needs. - notify-failure: watch compile-windows and build-windows so the thumbs-down / Build failed comment fires if either fails. Version format: RELEASE_VERSION must satisfy two constraints — get_repo.sh's ^([0-9]+\.[0-9]+\.[0-5])[0-9]+$ regex and Inno Setup's VersionInfoVersion (each dotted component an unsigned 16-bit int). Use MS_TAG + hour-of-year TIME_PATCH; carry PR number and short hash only in the release title / PR comment. Windows env: set OS_NAME=windows so build.sh and build_cli.sh take the Windows branches; CI_BUILD=yes so build/windows/package.sh doesn't exit early; SHOULD_BUILD_REH=no on both jobs to match.
4b48379 to
8c66b1c
Compare
|
Pre-release: 1.108.12718 (8c66b1c) https://github.com/genesis-ai-dev/codex/releases/tag/1.108.12718 |
- Override nameShort/nameLong/darwinBundleIdentifier in product.json
before the mac build so the .app is "Codex Beta.app" instead of
"Codex.app". prepare_vscode.sh hardcodes these regardless of
APP_NAME; the root product.json merge is the override point.
- Same override on the Windows compile job, plus win32DirName /
win32NameVersion / win32ShellNameShort so Start Menu & installer
show "Codex Beta".
- Release tag is now "\${VERSION}-pr\${PR_NUMBER}-\${SHORT_HASH}" so
repeat builds of the same PR don't collide. App-internal version
strings stay digits-only (Windows installer constraint).
Two-part change so that pinned extensions on conductor-managed profiles
aren't silently updated out from under us:
- patches/feat-codex-allow-profile-extension-updates.patch: drop
`scope: ConfigurationScope.APPLICATION` from `extensions.autoUpdate`
and `extensions.autoCheckUpdates`. Upstream locks these to the
application level, so setting them in a profile has no effect. With
APPLICATION removed the settings default to WINDOW scope and become
per-profile overridable. Side effect: all users (not just conductor
ones) gain per-profile control over these two keys.
- CodexConductor.seedProfileSettings: writes
extensions.auto{Update,CheckUpdates}=false into a conductor profile's
settings.json. Called before every profile switch, and as backfill on
startup when already sitting on a conductor profile (covers profiles
created before this change). Uses jsonEdit.setProperty + applyEdits
so any user-authored comments / formatting in the file survive.
Replaces the standalone `extension-sideloader` with a built-in Workbench Contribution. This new architecture installs extensions directly via the internal `IWorkbenchExtensionManagementService` during the `AfterRestored` phase, ensuring global extensions are ready before the Extension Host populates. Supports both Open VSX gallery IDs and direct VSIX URLs, with version-aware reinstallation for VSIXs. Installs are routed to the global location to ensure visibility across all VS Code profiles.
Improves the reliability of the build pipeline across local and CI environments. Updates `prepare_assets.sh` to safely execute locally without strictly requiring CI environment variables. Fixes TypeScript compilation issues (e.g., casting through unknown, guarding empty bundle arrays) and ensures DMG creation doesn't fail the build pipeline if `npx` encounters non-critical errors.
Introduces automated and manual workflows for building pre-release artifacts from PRs. Adds the `create-pr-release` script to generate VSIXs/builds with 'Codex Beta' branding for testing. Configures `.github/workflows/pr-build.yml` to be triggered via GitHub comments and utilizes `workflow_call` for reusable CI steps, standardizing the build environment variables.
…ration Updates the extension bundling lists for the new sideloader architecture. Removes `extension-sideloader` from `bundle-extensions.json`. Populates `codexSideloadExtensions` in `product.json` with the required global extensions, including switching `codex-editor` and `frontier-authentication` to specific pre-release VSIXs for beta testing. Bumps extension versions and fixes broken extension links.
b0ef2a8 to
ee0dffc
Compare
|
Pre-release: 1.108.12730 (ee0dffc) https://github.com/genesis-ai-dev/codex/releases/tag/1.108.12730-pr34-ee0dffc |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Introduces project-scoped extension version pinning and automated profile management to Codex.
Architecture Documentation
Changes
.gitignoreand local build scripts.AGENTS.mdto reflect new architecture.