Skip to content

feat(edge-worker): forward per-runner custom binary paths to spawned runners#1228

Open
mitekk wants to merge 1 commit into
cyrusagents:mainfrom
mitekk:feat/repository-pathToClaudeCodeExecutable
Open

feat(edge-worker): forward per-runner custom binary paths to spawned runners#1228
mitekk wants to merge 1 commit into
cyrusagents:mainfrom
mitekk:feat/repository-pathToClaudeCodeExecutable

Conversation

@mitekk
Copy link
Copy Markdown

@mitekk mitekk commented May 19, 2026

Motivation

Cyrus's three spawned-binary runners — claude, codex, gemini — each expose an SDK option for replacing the dispatched binary with a custom path:

Runner SDK option Default
claude pathToClaudeCodeExecutable (Anthropic SDK) bundled CLI
codex codexPath (cyrus-codex-runner) codex on PATH
gemini geminiPath (cyrus-gemini-runner) gemini on PATH

(The cursor runner is in-process via the Cursor SDK — no spawned binary, so no analogous knob.)

Useful when operators wrap the runner binary for:

  • Sandboxed / containerized dispatch (e.g. docker run sandbox …)
  • Per-invocation credential brokering or mode switching
  • Telemetry header injection or OTEL endpoint rewriting
  • Any transform that needs to fire on every runner invocation

Today, none of these options can be set from ~/.cyrus/config.json. cyrus-claude-runner, cyrus-codex-runner, and cyrus-gemini-runner each read their respective option from their own runner config, but nothing upstream of them in cyrus-edge-worker reads from cyrus config and forwards. So operators have to fork cyrus or replace the binary on PATH — neither great.

Change

Adds three optional per-repository fields to RepositoryConfigSchema: pathToClaudeCodeExecutable, codexPath, geminiPath. Each is forwarded by RunnerConfigBuilder.buildIssueConfig to the returned runner config when the resolved runner type matches. Naming matches each SDK's existing option, so the cyrus docs entry maps 1:1 to the SDK docs.

Field placement: per-repository, matching the existing pattern for model, fallbackModel, allowedTools, mcpConfigPath. No top-level fallback (kept the surface minimal; can be added in a follow-up).

Files

  • packages/core/src/config-schemas.ts — three optional fields with JSDoc
  • packages/edge-worker/src/RunnerConfigBuilder.ts — three runner-type-conditional forwards in buildIssueConfig's claude/codex/gemini blocks
  • packages/core/schemas/*.json — regenerated via pnpm --filter cyrus-core generate:json-schema
  • docs/CONFIG_FILE.md — documents the three fields under a "Custom runner-binary paths" subsection
  • packages/edge-worker/test/RunnerConfigBuilder.runner-binary-paths.test.ts — 9 tests: claude/codex/gemini × forwarded-when-set / omitted-when-unset / ignored-when-wrong-runner

Verification

pnpm build and pnpm test:packages:run both clean on this branch. 632 edge-worker tests pass (was 626 on main, +6 for the new codex/gemini variants); 129 core tests pass; suite-wide green.

Out of scope

  • executable field ("bun" vs "node" for claude) — separate concern; wrapper scripts that shebang bash or node work fine via the default.
  • Top-level config fallback — kept narrow for review.
  • Plumbing in buildChatConfig — only added to buildIssueConfig since that's where the per-runner-type branches live. Small follow-up if you want the same knob for Slack chat sessions.
  • Cursor runner — no spawned binary, no analogous SDK option.

Closes no issue — happy to file a separate one if you'd prefer.

…runners

Adds three optional per-repository config fields that flow through
RunnerConfigBuilder into the corresponding runner's config when the
resolved runner type matches:

  pathToClaudeCodeExecutable  → claude runner (SDK option name)
  codexPath                   → codex  runner
  geminiPath                  → gemini runner

Each runner SDK already accepts its respective option, but cyrus's
edge-worker / RunnerConfigBuilder never read them from
`~/.cyrus/config.json` and forwarded them. So today, operators wanting
to wrap a runner binary (sandbox dispatch, credential brokering,
telemetry header injection, etc.) have to fork cyrus or replace the
binary on PATH — neither great.

Scoped narrowly:
- Fields are per-repository (matches `model`, `fallbackModel`,
  `allowedTools`, etc.). No top-level fallback.
- Each is only honored for its matching runner type; silently ignored
  for the others. Same conditional shape as the existing per-runner
  fields (plugins, skills, sandbox, chrome extraArgs).
- The cursor runner is in-process (no spawned binary) and has no
  analogous knob — deliberately omitted.
- Schemas regenerated via `pnpm --filter cyrus-core generate:json-schema`.

Tests: 9 cases in
packages/edge-worker/test/RunnerConfigBuilder.runner-binary-paths.test.ts
(claude/codex/gemini × forwarded/omitted/wrong-runner-ignored).
@mitekk mitekk force-pushed the feat/repository-pathToClaudeCodeExecutable branch from ddd584b to 42d3372 Compare May 26, 2026 20:14
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.

1 participant