Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 68 additions & 0 deletions .github/workflows/_release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@ jobs:
release:
runs-on: ubuntu-24.04
timeout-minutes: 30
outputs:
version: ${{ steps.vars.outputs.version }}
dist_owner: ${{ steps.vars.outputs.dist_owner }}
dist_name: ${{ steps.vars.outputs.dist_name }}
steps:
- name: Checkout monorepo
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
Expand Down Expand Up @@ -165,6 +169,24 @@ jobs:
fi
fi

- name: Build and validate Codex submission from selected source
if: inputs.plugin == 'codex'
env:
RELEASE_VERSION: ${{ steps.vars.outputs.version }}
run: |
set -euo pipefail
make test-codex-packaging codex-submission
test -f "dist/submissions/braintrust-$RELEASE_VERSION.zip"

- name: Save Codex submission ZIP
if: inputs.plugin == 'codex'
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: braintrust-mcp-${{ inputs.source_sha }}-${{ steps.vars.outputs.version }}
path: dist/submissions/braintrust-${{ steps.vars.outputs.version }}.zip*
if-no-files-found: error
overwrite: true

- name: Record monorepo release
id: source-release
if: ${{ inputs.record }}
Expand Down Expand Up @@ -226,6 +248,52 @@ jobs:
--title "$DIST_TAG" \
--generate-notes

# Separate job: if asset upload fails after release creation, Re-run failed
# jobs retries attachment without repeating deployment/tag preflight.
attach-codex-submission:
needs: release
if: inputs.plugin == 'codex' && inputs.record
runs-on: ubuntu-24.04
timeout-minutes: 5
permissions:
contents: read
steps:
- name: Download validated submission from release build
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
name: braintrust-mcp-${{ inputs.source_sha }}-${{ needs.release.outputs.version }}
path: submission

- name: Generate distribution-repo token
id: app-token
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
with:
app-id: ${{ secrets.BRAINTRUST_BOT_APP_ID }}
private-key: ${{ secrets.BRAINTRUST_BOT_PRIVATE_KEY }}
owner: ${{ needs.release.outputs.dist_owner }}
repositories: ${{ needs.release.outputs.dist_name }}
permission-contents: write

- name: Attach MCP-only ZIP to distribution release
working-directory: submission
env:
GH_TOKEN: ${{ steps.app-token.outputs.token }}
DIST_REPO: ${{ inputs.dist_repo }}
VERSION: ${{ needs.release.outputs.version }}
run: |
set -euo pipefail
python3 - "$VERSION" <<'PY'
import hashlib
from pathlib import Path
import sys
archive = Path(f"braintrust-{sys.argv[1]}.zip")
expected = f"{hashlib.sha256(archive.read_bytes()).hexdigest()} {archive.name}\n"
if archive.with_suffix(".zip.sha256").read_text() != expected:
sys.exit("Submission ZIP checksum mismatch")
PY
gh release upload "v$VERSION" "braintrust-$VERSION.zip" "braintrust-$VERSION.zip.sha256" \
--repo "$DIST_REPO" --clobber

# Post-deploy end-to-end smoke test (codex only): install the just-deployed
# plugin from the marketplace on Linux + both macOS arches and assert a real
# session traces. It's a verification, not a gate — the deploy already happened.
Expand Down
13 changes: 13 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,14 @@ jobs:
fi
- name: Build and validate plugins
run: make test
- name: Test Codex packaging and release contracts
run: make test-codex-packaging codex-submission
- name: Save MCP submission ZIP
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: braintrust-mcp-submission
path: dist/submissions/braintrust-*.zip*
if-no-files-found: error
- name: Package Cursor extension
working-directory: dist/cursor
run: |
Expand Down Expand Up @@ -349,6 +357,11 @@ jobs:
npm exec --prefix "${{ runner.temp }}/coding-agents" -- claude --version
npm exec --prefix "${{ runner.temp }}/coding-agents" -- opencode --version
npm exec --prefix "${{ runner.temp }}/coding-agents" -- pi --version
- name: Check installed Codex MCP loading
if: runner.os == 'Linux'
env:
CODEX_BIN: ${{ runner.temp }}/coding-agents/node_modules/.bin/codex
run: make test-codex-mcp-loading
- name: Check formatting
if: runner.os == 'Linux'
run: cargo fmt --manifest-path bt-daemon/Cargo.toml -- --check
Expand Down
4 changes: 3 additions & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ jobs:
printf 'paths<<EOF\n.github/release-versions/%s\n' "$PLUGIN"
case "$PLUGIN" in
claude) printf '%s\n' 'src/plugins/claude/content/plugins/*/.claude-plugin/plugin.json' ;;
codex) printf '%s\n' 'src/plugins/codex/content/plugins/*/.codex-plugin/plugin.json' ;;
codex) printf '%s\n' 'src/plugins/codex/content/plugins/*/.codex-plugin/plugin.json' 'src/plugins/codex/content/plugins/*/plugin.json' ;;
cursor) printf '%s\n' 'src/plugins/cursor/content/.cursor-plugin/plugin.json' 'src/plugins/cursor/content/package.json' 'src/plugins/cursor/content/package-lock.json' ;;
grok) printf '%s\n' 'src/plugins/grok/content/.grok-plugin/plugin.json' 'src/plugins/grok/content/hooks/hooks.json' ;;
esac
Expand Down Expand Up @@ -145,6 +145,8 @@ jobs:

Cursor releases also publish `braintrustdata.braintrust` to Open VSX using trusted publishing.

Codex releases attach an MCP-only `braintrust-<version>.zip` to the distribution GitHub Release. OpenAI submission remains manual.

Human approval and merge into `main` authorize tagging the merge commit and publishing this version to the production destination. This preparation does not tag or deploy anything.

- name: Summarize release pull request
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# Build output
dist/
__pycache__/

# Local credentials and environment overrides
.env
13 changes: 13 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,16 @@ $(PUBLISH_RULES): publish-%:

clean:
@rm -rf "$(DIST)"

# MCP-only package for manual OpenAI submission. Never publish automatically.
.PHONY: codex-submission test-codex-packaging test-codex-mcp-loading
codex-submission: validate-codex
python3 src/plugins/codex/package.py zip "$(DIST)/codex" "$(DIST)/submissions"

test-codex-packaging:
python3 -m unittest discover -s src/plugins/codex/tests -p 'test_*.py' -v

# Requires Codex on PATH (or the integration harness's CODEX_BIN override).
# Loads the installed MCP entry without OAuth, inference, or tool calls.
test-codex-mcp-loading: validate-codex
node src/plugins/codex/tests/mcp-loading.mjs "$(DIST)/codex"
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ This monorepo contains the plugins and the shared Rust tracing daemon embedded
in the Braintrust CLI (`bt`). Plugins forward native events locally; the daemon
builds traces, journals events for recovery, and delivers spans to Braintrust.

This repository also packages the [Braintrust
plugin](src/plugins/codex/content/plugins/braintrust/README.md) for submission to
the official ChatGPT and Codex plugin directory. It connects to Braintrust's
hosted MCP server through OAuth and requires no `bt` installation. Installing it
does not enable session tracing.

## Get started

Install your coding agent and the
Expand Down Expand Up @@ -82,6 +88,9 @@ JavaScript packages, and stable Rust for the daemon. Run from the monorepo root:
```bash
make build # Build all plugins into dist/<agent>
make build-codex # Build one plugin
make codex-submission # Validate and build the MCP-only OpenAI ZIP
make test-codex-packaging # Test Codex packaging and release contracts
make test-codex-mcp-loading # Check installed MCP loading with a local Codex CLI
make test # Build and validate plugin packages and forwarders
cargo test --manifest-path bt-daemon/Cargo.toml --all-features --locked
```
Expand Down
33 changes: 20 additions & 13 deletions scripts/set-plugin-version.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
#!/usr/bin/env python3
"""Set a release version on every distributed version surface for an agent.

Versioning is per-plugin: every plugin under an agent carries its own
.<agent>-plugin/plugin.json with a `version` field. The marketplace manifest is
NOT touched.
Versioning is per distribution: portable root manifests and compatibility
manifests share one version. Marketplace manifests are NOT touched.

Only version values are rewritten, so surrounding files keep their formatting.

Expand All @@ -14,26 +13,34 @@
import re
import sys

# agent -> glob of its per-plugin manifests (relative to repo root)
# agent -> globs of its per-plugin manifests (relative to repo root)
MANIFEST_GLOBS = {
"claude": "src/plugins/claude/content/plugins/*/.claude-plugin/plugin.json",
"codex": "src/plugins/codex/content/plugins/*/.codex-plugin/plugin.json",
"grok": "src/plugins/grok/content/.grok-plugin/plugin.json",
"cursor": "src/plugins/cursor/content/.cursor-plugin/plugin.json",
"claude": ["src/plugins/claude/content/plugins/*/.claude-plugin/plugin.json"],
"codex": [
"src/plugins/codex/content/plugins/*/.codex-plugin/plugin.json",
"src/plugins/codex/content/plugins/*/plugin.json",
],
"grok": ["src/plugins/grok/content/.grok-plugin/plugin.json"],
"cursor": ["src/plugins/cursor/content/.cursor-plugin/plugin.json"],
}

VERSION_RE = re.compile(r'("version"\s*:\s*")[^"]*(")')
def main() -> None:
if len(sys.argv) != 3:
sys.exit("usage: set-plugin-version.py <agent> <version>")
agent, version = sys.argv[1], sys.argv[2].lstrip("v")
pattern = MANIFEST_GLOBS.get(agent)
if not pattern:
if not re.fullmatch(r"[0-9]+\.[0-9]+\.[0-9]+", version):
sys.exit("version must be MAJOR.MINOR.PATCH")
patterns = MANIFEST_GLOBS.get(agent)
if not patterns:
sys.exit(f"unknown agent '{agent}' (known: {', '.join(MANIFEST_GLOBS)})")

manifests = sorted(glob.glob(pattern))
if not manifests:
sys.exit(f"no plugin manifests found for '{agent}' ({pattern})")
manifests = []
for pattern in patterns:
matches = sorted(glob.glob(pattern))
if not matches:
sys.exit(f"no plugin manifests found for '{agent}' ({pattern})")
manifests.extend(matches)

surfaces = [(path, VERSION_RE, "version") for path in manifests]
if agent == "cursor":
Expand Down
1 change: 1 addition & 0 deletions src/plugins/codex/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#
# The Codex marketplace consumes a repo whose ROOT is the marketplace:
# .agents/plugins/marketplace.json marketplace manifest
# plugins/braintrust/ hosted MCP package (portable manifest)
# plugins/trace-codex/ tracing plugin (thin daemon hook shims)
#
# The tracing plugin contains no tracing runtime or platform-specific binary;
Expand Down
12 changes: 12 additions & 0 deletions src/plugins/codex/content/.agents/plugins/marketplace.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,18 @@
"displayName": "Braintrust Codex Plugin"
},
"plugins": [
{
"name": "braintrust",
"source": {
"source": "local",
"path": "./plugins/braintrust"
},
"policy": {
"installation": "AVAILABLE",
"authentication": "ON_INSTALL"
},
"category": "Developer Tools"
},
{
"name": "trace-codex",
"source": {
Expand Down
29 changes: 21 additions & 8 deletions src/plugins/codex/content/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,39 @@ Guidelines for AI agents working in this repo.

## Repo purpose

This repo distributes one [Codex marketplace plugin](https://developers.openai.com/codex/plugins):
This generated repo distributes two independently opt-in
[Codex marketplace plugins](https://developers.openai.com/plugins/build/plugins):

- `plugins/braintrust/` — hosted MCP with OAuth DCR, portable root `plugin.json`
and `mcp.json`. No hooks, apps, skills, credentials, or local runtime.

- `plugins/trace-codex/` — an opt-in plugin that traces Codex sessions to Braintrust (session, turn, and tool spans) via Codex lifecycle hooks.

The plugin is listed in `.agents/plugins/marketplace.json` (the repo marketplace).
Both plugins are listed in `.agents/plugins/marketplace.json`. Preserve the
marketplace name `braintrust-codex-plugins` and tracing identity
`trace-codex@braintrust-codex-plugins`.

Key files for the tracing plugin (see [`plugins/trace-codex/AGENTS.md`](plugins/trace-codex/AGENTS.md) for its architecture):
Key files for the tracing plugin:

- `plugins/trace-codex/AGENTS.md` — architecture and contributor guide for this plugin
- `plugins/trace-codex/README.md` — architecture and setup guide
- `plugins/trace-codex/.codex-plugin/plugin.json` — plugin manifest
- `plugins/trace-codex/hooks/hooks.json` — lifecycle hook config
- `plugins/trace-codex/src/` — the hook client + event server (compiled to `bin/codex-hook`)
- Tracing hooks forward to `bt trace hook --source codex`; there is no bundled runtime.

## Making changes

- **MCP metadata**: edit `plugins/braintrust/plugin.json` and `mcp.json` in the
source monorepo. Keep portable identity and OpenAI presentation in one manifest.
Keep server-level auth extensions out of the Codex `mcp.json`: current Codex
rejects them. The monorepo ZIP builder adds OAuth DCR metadata only to the
OpenAI submission archive and validates both formats separately.
- **Plugin metadata**: edit `plugins/trace-codex/.codex-plugin/plugin.json`.
- **Marketplace**: edit `.agents/plugins/marketplace.json` to change the plugin entry, category, or install policy.

## Releasing a new version

1. Bump `"version"` in the relevant plugin's `.codex-plugin/plugin.json`.
2. Commit, tag, and create a GitHub release (see README for exact commands).
3. Do **not** skip the git tag — releases are tracked via tags so users can see a changelog.
Make all source changes in `braintrustdata/braintrust-coding-agent-plugins`.
Both packages share the Codex distribution version. Use the monorepo's reviewed
Release Plugin workflow; do not edit or tag this generated repository manually.
The distribution release carries an MCP-only ZIP for manual OpenAI submission.
Template settings belong to portal setup, not speculative manifest fields.
27 changes: 23 additions & 4 deletions src/plugins/codex/content/README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,31 @@
# Braintrust tracing for Codex
# Braintrust plugins for Codex

> **This repository is generated.** It is built from
> [braintrustdata/braintrust-coding-agent-plugins](https://github.com/braintrustdata/braintrust-coding-agent-plugins).
> Don't edit files here — make changes and file issues in that repository, and they
> will be rebuilt into this one.

Trace Codex sessions in Braintrust with the `trace-codex` plugin.
This marketplace contains two independently opt-in plugins:

- **Braintrust**, `braintrust@braintrust-codex-plugins`: hosted MCP tools with
OAuth. No `bt` dependency or tracing hooks. See the
[MCP installation and endpoint guide](plugins/braintrust/README.md).
- **Trace Codex**, `trace-codex@braintrust-codex-plugins`: session tracing via `bt`.

For MCP, add this marketplace and select Braintrust:

```bash
codex plugin marketplace add braintrustdata/braintrust-codex-plugin
codex plugin add braintrust@braintrust-codex-plugins
```

The GitHub package connects to NA (US). EU and custom deployments require an
admin-configured local/team copy; GitHub installs do not inherit public portal
template settings. The planned public-directory listing uses one template,
pending OpenAI eligibility and endpoint-template confirmation. Its concrete NA
review URL is not a verified install-time default.

The sections below describe optional tracing setup only.

## Quickstart

Expand Down Expand Up @@ -49,5 +69,4 @@ bt trace disable codex
```

See the [tracing plugin guide](plugins/trace-codex/README.md) for hook behavior,
metadata, and tags. This marketplace does not configure the Braintrust MCP
server; use the agent's connector or MCP configuration for MCP access.
metadata, and tags. MCP sign-in and `bt` tracing sign-in are independent.
Loading
Loading