Skip to content

feat: add Codex + Copilot CLI harness backends#24

Merged
nhuelstng merged 4 commits into
mainfrom
feat/codex-copilot-backends
Jul 2, 2026
Merged

feat: add Codex + Copilot CLI harness backends#24
nhuelstng merged 4 commits into
mainfrom
feat/codex-copilot-backends

Conversation

@nhuelstng

Copy link
Copy Markdown
Contributor

Summary

Add Codex CLI (codex) and GitHub Copilot CLI (copilot) as supported harness backends in omac, following the same declarative pattern used for OpenCode and Claude Code. Brings the total to four supported agentic coders.

Changes

Go implementation (TDD):

  • internal/config/harness.go — codex + copilot Harness descriptors, SessionListCodex/SessionListCopilot enum values
  • internal/session/session.golistCodex() (reads ~/.codex/sessions/), listCopilot() (reads ~/.copilot/session-store.db via sqlite3 CLI)
  • internal/manifest/manifest.go — shared Render() function extracted (handles ready/pending-credentials/broken states + global target_path warning)
  • internal/sandboxprofile/resolve.go~/.codex + ~/.copilot added to default filesystem allow-list

Bridge scripts:

  • .codex/hooks/omac-bridge.sh + .codex/hooks.json — SessionStart only (Stop is turn-scoped, no deactivation; relies on TTL reaper)
  • .copilot/hooks/omac-bridge.sh + .copilot/hooks/omac.json — SessionStart + SessionEnd, user-level hooks

Docs:

  • README.md — harness tables, bridge mechanisms, quickstart, continue/resume examples, sandbox profile
  • CREATING_A_SKILL.md — skills dirs, discovery section, sidecar section for all 4 harnesses
  • Design spec + implementation plan in docs/superpowers/
  • Openspec change proposal in openspec/changes/support-codex-copilot-harnesses/

Key decisions

  • Codex has NO deactivate on Stop — Stop event is turn-scoped, not session-scoped. Relies on TTL reaper.
  • Copilot uses PascalCase event names (SessionStart/SessionEnd) for snake_case payload compatibility
  • Copilot registration at user-level .copilot/hooks/omac.json (not repo-level .github/hooks/)
  • Copilot alias is co (exec is standalone copilot binary, not gh-copilot)
  • sqlite3 CLI for copilot session listing (opencode already shells out to sqlite3)

Test plan

  • go build ./... passes
  • go test ./... passes (all packages green)
  • Harness descriptor tests (codex + copilot lookup, fields, session metadata)
  • Session listing dispatch tests (codex + copilot dispatch, missing-store handling)
  • Manifest generator tests (Render with ready/pending/broken/global skills)
  • Bridge script tests (codex + copilot: exists, runs, inert when omac absent)
  • TestBundleGuideMatchesRepoRoot — bundled guide synced with repo root

Deferred to separate change

  • omac manifest subcommand + migration of existing bridges
  • COPILOT_HOME env override support
  • Pre-flight binary check (exec.LookPath)

Copilot AI 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.

Pull request overview

This PR expands omac’s harness ecosystem by adding Codex CLI and GitHub Copilot CLI as first-class harness backends, plus follow-on CLI/config improvements (manifest rendering command, inner-binary preflight checks, and *_HOME config-home overrides) and associated docs/bridge assets.

Changes:

  • Add codex/copilot harness descriptors, session-list kinds, and session listing implementations.
  • Extract a shared skills-manifest renderer (internal/manifest.Render) and expose it via omac manifest.
  • Add preflight inner-harness binary checks (start/serve) and advisory harness-binary reporting in omac doctor, plus docs/specs/bridge hook assets.

Reviewed changes

Copilot reviewed 29 out of 30 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
README.md Documents new harness tokens/aliases and updates usage examples and harness tables.
openspec/changes/support-codex-copilot-harnesses/tasks.md Adds/records implementation task tracking for codex+copilot and related follow-ons.
openspec/changes/support-codex-copilot-harnesses/proposal.md Adds an OpenSpec proposal describing codex+copilot harness support.
internal/session/session.go Adds Codex/Copilot session store discovery and listing dispatch.
internal/session/session_test.go Extends session-listing tests to cover new harnesses and missing-store behavior.
internal/sandboxprofile/resolve.go Allows default sandbox filesystem access to ~/.codex and ~/.copilot.
internal/manifest/manifest.go Introduces a harness-agnostic manifest renderer for activate-response JSON.
internal/manifest/manifest_test.go Adds unit tests for manifest rendering across skill states.
internal/config/harness.go Registers codex/copilot harnesses; adds HomeEnv + ConfigHome() and wires global dirs through it.
internal/config/harness_test.go Adds tests for codex/copilot descriptors and *_HOME overrides.
internal/cli/start.go Adds shared checkInnerBinary and wires it into omac start launch flow.
internal/cli/serve.go Wires inner-binary preflight check into omac serve.
internal/cli/manifest_cmd.go Adds omac manifest subcommand for rendering manifest text from activate JSON.
internal/cli/manifest_cmd_test.go Adds tests for omac manifest (stdin/file/invalid/misuse cases).
internal/cli/doctor.go Adds an “Inner harnesses” section reporting binary presence on PATH.
internal/cli/cli.go Registers the new manifest subcommand and updates usage text.
internal/cli/binary_check_test.go Adds unit tests for checkInnerBinary and doctor output coverage.
internal/builtinskills/assets/omac-write-a-skill/references/creating-a-skill.md Updates bundled guidance doc to mention codex/copilot harness directories and bridges.
docs/superpowers/specs/2026-06-29-deferred-items-design.md Adds a design/spec for the “deferred items” (manifest cmd, binary checks, env overrides).
docs/superpowers/specs/2026-06-29-codex-copilot-backends-design.md Adds a detailed design spec for codex/copilot harness support.
docs/superpowers/plans/2026-06-29-deferred-items.md Adds an implementation plan for deferred items (now implemented in this PR).
docs/superpowers/plans/2026-06-29-codex-copilot-backends.md Adds an implementation plan for remaining codex/copilot work.
CREATING_A_SKILL.md Updates public skill authoring doc for codex/copilot directories and bridges.
.gitignore Ignores local /.worktrees/ directory.
.copilot/hooks/omac.json Adds Copilot hook registration JSON (SessionStart/SessionEnd).
.copilot/hooks/omac-bridge.sh Adds Copilot bridge hook script (activate/deactivate + manifest injection).
.copilot/hooks/bridge_test.go Adds a Go test intended to sanity-check the Copilot bridge script.
.codex/hooks/omac-bridge.sh Adds Codex bridge hook script (activate + manifest injection; no deactivate).
.codex/hooks/bridge_test.go Adds a Go test intended to sanity-check the Codex bridge script.
.codex/hooks.json Adds Codex hook registration JSON (SessionStart only).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread README.md Outdated
Comment thread openspec/changes/support-codex-copilot-harnesses/proposal.md Outdated
Comment thread openspec/changes/support-codex-copilot-harnesses/tasks.md Outdated
Comment thread docs/superpowers/specs/2026-06-29-deferred-items-design.md Outdated
Comment thread internal/session/session.go
Comment thread .copilot/hooks/bridge_test.go Outdated
Comment thread internal/bridge/bridge_test.go

Copilot AI 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.

Pull request overview

Copilot reviewed 31 out of 32 changed files in this pull request and generated 12 comments.

Comments suppressed due to low confidence (1)

README.md:399

  • These rows are now inaccurate: harness-specific directories (including ~/.claude, ~/.codex, ~/.copilot, and OpenCode’s config/runtime dirs) are no longer granted by the default profile and are injected per-harness at launch time. Also ~/.claude.json is no longer in the default profile read list.
| `~/.claude`, `~/.codex`, `~/.copilot` | read+write | default profile `filesystem.allow` |
| `~/.cache`, `~/Library/Caches` | read+write | default profile `filesystem.allow` |
| `~/go`, `~/.rustup`, `~/.cargo` | read+write | default profile `filesystem.allow` |
| `~/.config/opencode`, `~/.opencode/bin` | read-only | default profile `filesystem.read` |
| `~/.nvm`, `~/.gitconfig`, `~/.gitignore_global`, `~/.claude.json` | read-only | default profile `filesystem.read` |

Comment thread README.md Outdated
Comment thread README.md Outdated
Comment thread internal/session/session.go
Comment thread internal/session/session.go Outdated
Comment thread internal/session/session.go Outdated
Comment thread openspec/changes/support-codex-copilot-harnesses/proposal.md Outdated
Comment thread docs/superpowers/specs/2026-06-29-deferred-items-design.md
Comment thread internal/bridge/bridge_test.go
Comment thread .copilot/hooks/bridge_test.go Outdated
Comment thread README.md Outdated

Copilot AI 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.

Pull request overview

Copilot reviewed 32 out of 34 changed files in this pull request and generated 9 comments.

Comments suppressed due to low confidence (1)

README.md:399

  • The sandbox visibility table is now out of sync with the updated sandbox profile + harness-injected SandboxDirs. ~/.config/opencode / ~/.opencode/bin are no longer part of the default profile filesystem.read, and harness runtime dirs are granted read+write via injected --allow flags instead. Please update this table to reflect the new sources/permissions (and keep ~/.claude.json listed if it remains required).
| Selected harness config/state dirs (e.g. `~/.claude`, `~/.codex`, `~/.copilot`, `~/.local/share/opencode`) | read+write | `harness.SandboxDirs` → `--allow` flags (injected at launch) |
| `~/.cache`, `~/Library/Caches` | read+write | default profile `filesystem.allow` |
| `~/go`, `~/.rustup`, `~/.cargo` | read+write | default profile `filesystem.allow` |
| `~/.config/opencode`, `~/.opencode/bin` | read-only | default profile `filesystem.read` |
| `~/.nvm`, `~/.gitconfig`, `~/.gitignore_global`, `~/.claude.json` | read-only | default profile `filesystem.read` |

Comment thread README.md
Comment on lines +521 to +524
`--session-id <id>`). Sessions come from the harness's own
store (opencode `session list`; Claude Code's
~/.claude/projects files; codex `codex session list`; copilot
`copilot session list`). Non-interactive stdin prints the
Comment on lines 35 to 39
Read: []string{
"~/.config/opencode",
"~/.opencode/bin",
"~/.gitconfig",
"~/.gitignore_global",
"~/.nvm",
"~/.bun/bin",
Comment thread .codex/hooks.json
Comment on lines +7 to +11
{
"type": "command",
"command": "bash \"$(git rev-parse --show-toplevel)/.codex/hooks/omac-bridge.sh\"",
"statusMessage": "Loading omac skills"
}
Comment thread .copilot/hooks/omac.json
Comment on lines +5 to +10
{
"type": "command",
"bash": "bash \"$(git rev-parse --show-toplevel)/.copilot/hooks/omac-bridge.sh\"",
"cwd": ".",
"timeoutSec": 30
}
Comment thread .copilot/hooks/omac.json
Comment on lines +13 to +18
{
"type": "command",
"bash": "bash \"$(git rev-parse --show-toplevel)/.copilot/hooks/omac-bridge.sh\"",
"cwd": ".",
"timeoutSec": 10
}
Comment on lines +3 to +9
import (
"encoding/json"
"os/exec"
"path/filepath"
"strings"
"testing"
)
Comment on lines +17 to +26
// The bridge is inert when OMAC_CONTROL_BASE is unset, so we set it to
// a dummy value that will cause activation to fail gracefully (curl
// error → empty manifest → no output). To test the manifest rendering
// path, we need a live control plane. Instead, test that the bridge:
// 1. Exists and is executable
// 2. Accepts a SessionStart payload without error
// 3. Is inert (no output) when control plane is unreachable
//
// This is the minimal check: the script runs, doesn't crash, and is
// a no-op when the control plane is absent.
Comment on lines +53 to +57
func TestCodexBridgeScriptExists(t *testing.T) {
if _, err := exec.Command("test", "-f", codexBridgePath).Output(); err != nil {
t.Skipf("codex bridge script not found at %s", codexBridgePath)
}
}
Comment on lines +86 to +90
func TestCopilotBridgeScriptExists(t *testing.T) {
if _, err := exec.Command("test", "-f", copilotBridgePath).Output(); err != nil {
t.Skipf("copilot bridge script not found at %s", copilotBridgePath)
}
}
@nhuelstng nhuelstng force-pushed the feat/codex-copilot-backends branch 2 times, most recently from da94a26 to ca0f9c1 Compare July 1, 2026 15:00
- Harness descriptors for codex and copilot with session listing
- Bridge scripts for codex (--c instructions) and copilot (custom dirs)
- Sandbox briefing injection for both harnesses
- Manifest subcommand, binary check, env overrides, doctor improvements
- Shebang detection for script binaries
- Shared manifest generator extracted to internal/manifest
- Docs: README, CREATING_A_SKILL, design specs, implementation plans

Signed-off-by: Niclas Hülsmann <niclas.huelsmann@tngtech.com>
@nhuelstng nhuelstng force-pushed the feat/codex-copilot-backends branch from ca0f9c1 to b6fa0a9 Compare July 1, 2026 15:02
nhuelstng added 3 commits July 2, 2026 14:35
E2E test suite for the omac sandbox: verifies that each harness
(opencode, claude-code, codex, copilot) can start under the omac
sandbox and run an agent that calls bundled skills.

Two test flows:
- TestE2EEchoRest: agent calls echo-rest skill /status endpoint,
  asserts {"ok":true} response.
- TestE2ESecurityAudit: agent runs a shell-script audit probe
  (scripts/audit.sh) that tests secret isolation, env var filtering,
  filesystem confinement, and network egress blocking.

Security audit design:
- AllowanceSpec (internal/e2e/allowance.go) — human-readable Go
  struct declaring what the agent IS and IS NOT allowed to see/do.
  Drives both the sandbox profile (environment.allow_vars) and the
  test assertions (positive + negative).
- self-audit skill: sidecar holds AUDIT_SECRET via env_passthrough;
  agent runs probes; test asserts on raw OS denial messages in
  captured output — no LLM judgment for pass/fail.
- Negative assertions: AUDIT_SECRET not leaked, denied env vars
  stripped, filesystem denied, network denied.
- Positive assertions: sidecar reachable, allow-listed env vars
  visible.
- codex on macOS: --no-sandbox (Rust HTTP client incompatible with
  Seatbelt); skips negative assertions, runs positive only.

CI artifacts: each test writes agent stdout/stderr, sidecar logs,
sandbox profile, and metadata to E2E_LOG_DIR, uploaded as CI
artifact for post-mortem analysis (14-day retention).

Model: zai-org/GLM-5.2 via SKAINET_EXTERNAL endpoint for all
harnesses except claude-code (claude-sonnet-5 via Anthropic proxy).

Results (run 28590006482): 7/8 SecurityAudit PASS, only
opencode/macOS flaked (LLM didn't run full script). All EchoRest
tests pass on all 8 jobs.

Signed-off-by: Niclas Hülsmann <niclas.huelsmann@tngtech.com>
Signed-off-by: Niclas Hülsmann <niclas.huelsmann@tngtech.com>
staticcheck runs without the e2e build tag, so it flagged all
functions/types in the e2e package as unused. Add //go:build e2e
to allowance.go, artifacts.go, harnesses.go, versions.go, and
harnesses_test.go.

Also fix S1011 (replace loop with append).

Signed-off-by: Niclas Hülsmann <niclas.huelsmann@tngtech.com>
@nhuelstng nhuelstng merged commit c0dd3ee into main Jul 2, 2026
4 checks passed
@nhuelstng nhuelstng deleted the feat/codex-copilot-backends branch July 7, 2026 09:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants