Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Feature: configurable reasoning effort (effort) for Claude runner #1105

@van4oza

Description

@van4oza

Summary

Cyrus currently has no way to configure the Claude Agent SDK's reasoning effort level (low / medium / high / max). The capability exists in every layer beneath cyrus, but the edge worker doesn't plumb it through.

Current state (v0.2.44)

Layer Supports effort?
Claude Code CLI --effort <level>
@anthropic-ai/claude-agent-sdk effort query option + extraArgs passthrough
cyrus-claude-runner ✅ Forwards config.extraArgs → SDK query options
cyrus-edge-worker ❌ Hardcodes extraArgs: { chrome: null } in RunnerConfigBuilder.ts

So the whole stack could carry an effort value — cyrus just never reads one from config.

Also, RunnerSelectionService.determineRunnerSelection() only parses [agent=...] and [model=...] description tags — no effort tag.

Proposed change

Match the existing model selection precedence:

  1. Description tag[effort=low|medium|high|max] in the Linear issue description
  2. Issue labeleffort-low, effort-medium, effort-high, effort-max
  3. Per-repository configrepository.extraArgs.effort (or a dedicated repository.effort field) in ~/.cyrus/config.json
  4. (Optional) Global defaultclaudeDefaultEffort alongside claudeDefaultModel

Only applies to the Claude runner (the SDK's effort knob is Claude-specific).

Proposed code changes

RunnerSelectionService.ts — parse effort from description/labels and return it:

const descriptionEffortTagRaw = this.parseDescriptionTag(normalizedDescription, "effort");
const resolveEffortFromLabel = (lowercaseLabels: string[]) => {
  for (const lvl of ["low", "medium", "high", "max"] as const) {
    if (lowercaseLabels.includes(`effort-${lvl}`)) return lvl;
  }
  return undefined;
};
let effortOverride = (descriptionEffortTagRaw || "").toLowerCase().trim()
  || resolveEffortFromLabel(normalizedLabels);
if (effortOverride && !["low","medium","high","max"].includes(effortOverride)) {
  effortOverride = undefined;
}
if (runnerType !== "claude") effortOverride = undefined;

return { runnerType, modelOverride, fallbackModelOverride, effortOverride };

RunnerConfigBuilder.ts — stop hardcoding extraArgs, merge from repo config and selector:

...(runnerType === "claude" && {
  extraArgs: {
    chrome: null,
    ...(input.repository.extraArgs || {}),
    ...(runnerSelection.effortOverride ? { effort: runnerSelection.effortOverride } : {}),
  },
}),

This also unlocks repository.extraArgs as a general escape hatch for any future Claude Code CLI flag (--agent, --betas, etc.) without needing code changes each time.

Use cases

  • A user on a Max plan wants Opus + effort=max for heavy refactoring on one repo, and Sonnet + effort=low for routine triage on another.
  • Per-issue: a one-off research task tagged [effort=max] vs typical issues defaulting to high.
  • Cost control: teams running many concurrent sessions can default to effort=medium globally while letting specific hard issues opt into max via description tag.

Notes

I've applied this patch locally to cyrus-edge-worker/dist/ in my install and verified it works end-to-end with pm2 restart cyrus (status endpoint clean, no runtime errors). Happy to turn this into a PR if the approach looks right.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions