Skip to content
Merged
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
80 changes: 75 additions & 5 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@ jobs:
with:
node-version: 24
cache: pnpm
cache-dependency-path: src/plugins/opencode/content/pnpm-lock.yaml
cache-dependency-path: |
src/plugins/opencode/content/pnpm-lock.yaml
src/plugins/pi/content/pnpm-lock.yaml
- name: Reject Bun tooling in OpenCode and the shared client
shell: bash
run: |
Expand Down Expand Up @@ -66,8 +68,8 @@ jobs:
run: node --test src/runtime/js-daemon-client/tests/client.test.ts
- name: Verify generated client preparation
run: |
node scripts/prepare-js-daemon-client.mjs opencode
node scripts/prepare-js-daemon-client.mjs opencode --check
node scripts/prepare-js-daemon-client.mjs all
node scripts/prepare-js-daemon-client.mjs all --check

opencode-compatibility:
name: OpenCode package (${{ matrix.peer-version }})
Expand Down Expand Up @@ -107,6 +109,57 @@ jobs:
node -e 'import(process.argv[1]).then((m) => { if (typeof m.default !== "function") process.exit(1) })' \
"$PWD/install-test/node_modules/@braintrust/trace-opencode/dist/index.mjs"

resolve-pi-compatibility:
name: Resolve Pi compatibility window
runs-on: ubuntu-24.04
timeout-minutes: 10
outputs:
versions: ${{ steps.versions.outputs.versions }}
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
- id: versions
name: Resolve latest patch from five Pi minor lines
env:
PI_VERSION_COUNT: 5
run: node src/plugins/pi/content/scripts/resolve-pi-versions.mjs

pi-compatibility:
name: Pi package (${{ matrix.pi-target.version }})
needs: resolve-pi-compatibility
strategy:
fail-fast: false
matrix:
pi-target: ${{ fromJson(needs.resolve-pi-compatibility.outputs.versions) }}
runs-on: ubuntu-24.04
timeout-minutes: 20
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
- name: Set up pnpm
uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0
with:
version: 11.21.0
- name: Set up Node
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: 24
cache: pnpm
cache-dependency-path: src/plugins/pi/content/pnpm-lock.yaml
- name: Build and validate Pi package
run: make validate-pi
- name: Test resolved Pi compatibility target
shell: bash
run: |
set -euo pipefail
package_dir="$(mktemp -d)"
(cd dist/pi && pnpm pack --pack-destination "$package_dir" >/dev/null)
tarball="$(find "$package_dir" -name 'braintrust-pi-extension-*.tgz' -print -quit)"
mkdir pi-install-test
npm install --prefix pi-install-test --no-audit --no-fund \
"$tarball" \
'${{ matrix.pi-target.piCodingAgentSpec }}'
node -e 'import(process.argv[1]).then((m) => { if (typeof m.default !== "function") process.exit(1) })' \
"$PWD/pi-install-test/node_modules/@braintrust/pi-extension/dist/index.mjs"

daemon:
name: Daemon (${{ matrix.os }})
strategy:
Expand All @@ -129,7 +182,7 @@ jobs:
rustup default stable
rustup component add clippy rustfmt
- name: Install latest coding agents
run: npm install --prefix "${{ runner.temp }}/coding-agents" --no-save --no-package-lock --no-audit --no-fund --cache "${{ runner.temp }}/npm-cache" @openai/codex@latest @anthropic-ai/claude-code@latest opencode-ai@latest
run: npm install --prefix "${{ runner.temp }}/coding-agents" --no-save --no-package-lock --no-audit --no-fund --cache "${{ runner.temp }}/npm-cache" @openai/codex@latest @anthropic-ai/claude-code@latest opencode-ai@latest @earendil-works/pi-coding-agent@latest
- name: Set up pnpm
uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0
with:
Expand All @@ -139,7 +192,9 @@ jobs:
with:
node-version: 24
cache: pnpm
cache-dependency-path: src/plugins/opencode/content/pnpm-lock.yaml
cache-dependency-path: |
src/plugins/opencode/content/pnpm-lock.yaml
src/plugins/pi/content/pnpm-lock.yaml
- name: Build and install OpenCode plugin for integration harness
shell: bash
run: |
Expand All @@ -154,11 +209,24 @@ jobs:
"$tarball" @opencode-ai/plugin@latest @opencode-ai/sdk@latest
echo "OPENCODE_PLUGIN=$install_dir/node_modules/@braintrust/trace-opencode/dist/index.mjs" \
>> "$GITHUB_ENV"
- name: Build and install Pi extension for integration harness
shell: bash
run: |
set -euo pipefail
make build-pi
package_dir="$RUNNER_TEMP/pi-package"
install_dir="$RUNNER_TEMP/pi-extension"
mkdir -p "$package_dir" "$install_dir"
(cd dist/pi && pnpm pack --pack-destination "$package_dir" >/dev/null)
tarball="$(find "$package_dir" -name 'braintrust-pi-extension-*.tgz' -print -quit)"
npm install --prefix "$install_dir" --no-save --no-package-lock --no-audit --no-fund \
"$tarball"
- name: Report coding-agent versions
run: |
npm exec --prefix "${{ runner.temp }}/coding-agents" -- codex --version
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 formatting
if: runner.os == 'Linux'
run: cargo fmt --manifest-path bt-daemon/Cargo.toml -- --check
Expand All @@ -173,6 +241,8 @@ jobs:
CODEX_BIN: ${{ runner.temp }}/coding-agents/node_modules/.bin/codex${{ matrix.agent_suffix }}
CLAUDE_BIN: ${{ runner.temp }}/coding-agents/node_modules/.bin/claude${{ matrix.agent_suffix }}
OPENCODE_BIN: ${{ runner.temp }}/coding-agents/node_modules/.bin/opencode${{ matrix.agent_suffix }}
PI_BIN: ${{ runner.temp }}/coding-agents/node_modules/.bin/pi${{ matrix.agent_suffix }}
PI_EXTENSION_PATH: ${{ runner.temp }}/pi-extension/node_modules/@braintrust/pi-extension/dist/index.mjs
run: cargo test --manifest-path bt-daemon/Cargo.toml --all-features --locked --test agent_integration -- --ignored --nocapture --test-threads=1
- name: Lint daemon
run: cargo clippy --manifest-path bt-daemon/Cargo.toml --all-targets --all-features --locked -- -D warnings
141 changes: 141 additions & 0 deletions .github/workflows/release-pi.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
# GENERATED by sdk-actions `bin/workflow` — update with: bin/workflow update <this file>
# sdk-actions: {"template":"release/js/turnkey","ref":"71731d4a726df3e3bf42e50c2da4a75842b038dd","version":"1.0.0","params":{"access":"public","allowed_channels":"latest,rc,next,beta","dry_run_environment":"npm-pi-dry-run","emoji":":javascript:","node_version":"src/plugins/pi/content/.tool-versions","package_name":"@braintrust/pi-extension","provenance":"true","publish_environment":"npm-pi","slack_channel_var":"SLACK_SDK_RELEASE_CHANNEL","slack_token_secret":"SLACK_BOT_TOKEN","workflow_name":"Release Pi","working_directory":"src/plugins/pi/content"}}

name: Release Pi

# Releases @braintrust/pi-extension to npm via braintrustdata/sdk-actions. Dispatch after merging
# the commit that bumps package.json's "version"; the release runs against that commit's SHA.
on:
workflow_dispatch:
inputs:
release_type:
description: "Release type (stable → GitHub release; prerelease → none)"
type: choice
default: stable
options: [stable, prerelease]
channel:
description: "npm dist-tag / channel"
type: choice
default: latest
options: [latest, rc, next, beta]
sha:
description: "Commit SHA (of the version bump) to release"
required: true
type: string
prev_release:
description: "Release-notes anchor: a tag or SHA (empty → the previous tag)"
type: string
required: false
dry_run:
description: "Dry run: build without tagging or publishing"
type: boolean
default: false

jobs:
# FACT-FIND — derive version/tag/notes, check the registry, and emit the package manifest.
# contents: write is for the releases/generate-notes API.
configure:
runs-on: ubuntu-24.04
timeout-minutes: 5
permissions:
contents: write
outputs:
version: ${{ steps.configure.outputs.version }}
release_tag: ${{ steps.configure.outputs.release_tag }}
prev_release: ${{ steps.configure.outputs.prev_release }}
branch: ${{ steps.configure.outputs.branch }}
on_release_branch: ${{ steps.configure.outputs.on_release_branch }}
commit_message: ${{ steps.configure.outputs.commit_message }}
channel: ${{ steps.configure.outputs.channel }}
github_release: ${{ steps.configure.outputs.github_release }}
already_published: ${{ steps.configure.outputs.already_published }}
notes: ${{ steps.configure.outputs.notes }}
package: ${{ steps.configure.outputs.package }}
steps:
- name: Configure release
id: configure
uses: braintrustdata/sdk-actions/actions/release/lang/js/configure@71731d4a726df3e3bf42e50c2da4a75842b038dd
with:
sha: ${{ inputs.sha }}
working_directory: src/plugins/pi/content
tag_format: 'v{version}-pi'
release_type: ${{ inputs.release_type }}
npm_channel: ${{ inputs.channel }}
npm_package_name: '@braintrust/pi-extension'
package_label: '@braintrust/pi-extension'
emoji: ':javascript:'
prev_release: ${{ inputs.prev_release }}

validate:
needs: [configure]
runs-on: ubuntu-24.04
timeout-minutes: 10
permissions:
contents: read
steps:
- name: Validate release
uses: braintrustdata/sdk-actions/actions/release/lang/js/validate@71731d4a726df3e3bf42e50c2da4a75842b038dd
with:
sha: ${{ inputs.sha }}
dry_run: ${{ inputs.dry_run }}
working_directory: src/plugins/pi/content
node_version: src/plugins/pi/content/.tool-versions
release_tag: ${{ needs.configure.outputs.release_tag }}
channel: ${{ needs.configure.outputs.channel }}
allowed_channels: 'latest,rc,next,beta'
on_release_branch: ${{ needs.configure.outputs.on_release_branch }}
notes: ${{ needs.configure.outputs.notes }}
already_published: ${{ needs.configure.outputs.already_published }}
enforce_release_branch: 'true'

request-approval:
needs: [configure, validate]
runs-on: ubuntu-24.04
timeout-minutes: 5
permissions: {}
steps:
- name: Request release approval
uses: braintrustdata/sdk-actions/actions/release/request-approval@71731d4a726df3e3bf42e50c2da4a75842b038dd
with:
packages: '{"packages":[${{ needs.configure.outputs.package }}]}'
sha: ${{ inputs.sha }}
branch: ${{ needs.configure.outputs.branch }}
on_release_branch: ${{ needs.configure.outputs.on_release_branch }}
commit_message: ${{ needs.configure.outputs.commit_message }}
dry_run: ${{ inputs.dry_run }}
slack_token: ${{ secrets.SLACK_BOT_TOKEN }}
slack_channel: ${{ vars.SLACK_SDK_RELEASE_CHANNEL }}

build-and-ship:
needs: [configure, validate, request-approval]
runs-on: ubuntu-24.04
timeout-minutes: 15
environment: ${{ inputs.dry_run && 'npm-pi-dry-run' || 'npm-pi' }}
permissions:
contents: write
id-token: write # OIDC trusted publishing + provenance
attestations: write # signed SBOM attestation
steps:
- name: Build and ship
uses: braintrustdata/sdk-actions/actions/release/lang/js/build-and-ship@71731d4a726df3e3bf42e50c2da4a75842b038dd
with:
sha: ${{ inputs.sha }}
working_directory: src/plugins/pi/content
node_version: src/plugins/pi/content/.tool-versions
dry_run: ${{ inputs.dry_run }}
release_tag: ${{ needs.configure.outputs.release_tag }}
already_published: ${{ needs.configure.outputs.already_published }}
github_release: ${{ needs.configure.outputs.github_release }}
version: ${{ needs.configure.outputs.version }}
access: 'public'
provenance: 'true'
channel: ${{ needs.configure.outputs.channel }}
package_name: '@braintrust/pi-extension'
label: '@braintrust/pi-extension'
notes: ${{ needs.configure.outputs.notes }}
prev_release: ${{ needs.configure.outputs.prev_release }}
branch: ${{ needs.configure.outputs.branch }}
on_release_branch: ${{ needs.configure.outputs.on_release_branch }}
slack_token: ${{ secrets.SLACK_BOT_TOKEN }}
slack_channel: ${{ vars.SLACK_SDK_RELEASE_CHANNEL }}
emoji: ':javascript:'
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ For further instructions, see the instructions for your desired coding agent
| Claude Code | [braintrustdata/braintrust-claude-plugin](https://github.com/braintrustdata/braintrust-claude-plugin) |
| Codex | [braintrustdata/braintrust-codex-plugin](https://github.com/braintrustdata/braintrust-codex-plugin) |
| OpenCode | npm: [`@braintrust/trace-opencode`](https://www.npmjs.com/package/@braintrust/trace-opencode) |
| Pi | npm: [`@braintrust/pi-extension`](https://www.npmjs.com/package/@braintrust/pi-extension) |

## Development & releasing

Expand Down
2 changes: 1 addition & 1 deletion bt-daemon/docs/protocol.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ Result:
{
"protocol_version": 1,
"daemon_version": "0.1.0",
"capabilities": { "sources": ["codex", "claude-code", "opencode", "debug"] }
"capabilities": { "sources": ["codex", "claude-code", "opencode", "pi", "debug"] }
}
```
If `protocol_version` is incompatible the daemon returns an application error;
Expand Down
5 changes: 4 additions & 1 deletion bt-daemon/src/translate/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,13 @@ mod codex;
mod debug;
mod git;
mod opencode;
mod pi;

pub use claude::ClaudeTranslatorFactory;
pub use codex::CodexTranslatorFactory;
pub use debug::DebugTranslatorFactory;
pub use opencode::OpenCodeTranslatorFactory;
pub use pi::PiTranslatorFactory;

use crate::wire::{Envelope, SessionConfig};
use serde::{Deserialize, Serialize};
Expand Down Expand Up @@ -125,7 +127,8 @@ impl Registry {
let git = Arc::new(git::GitMetadataCache::default());
r.register(Box::new(ClaudeTranslatorFactory::new(git.clone())));
r.register(Box::new(CodexTranslatorFactory::new(git.clone())));
r.register(Box::new(OpenCodeTranslatorFactory::new(git)));
r.register(Box::new(OpenCodeTranslatorFactory::new(git.clone())));
r.register(Box::new(PiTranslatorFactory::new(git)));
r
}

Expand Down
Loading
Loading