Skip to content
Merged
3 changes: 1 addition & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -173,5 +173,4 @@ OPUS_ANALYSIS_AND_IDEAS.md
.security-key
/shared_docs
logs/security/
Agents.md
packages/
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

The removal of packages/ from the .gitignore file is a significant change that could have unintended consequences, such as committing node modules or other dependencies into the repository. This change seems unrelated to the PR's goal of adding an expand button. Could you please clarify the reason for this change or revert it if it was unintentional?

Agents.md
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

❓ Verification inconclusive

Script executed:

# Check git history for .gitignore to see what happened with Agents.md
git log --oneline -20 -- .gitignore

Repository: AndyMik90/Auto-Claude


Repository: AndyMik90/Auto-Claude
Exit code: 0

stdout:

1205444 fix: remove unrelated changes from branch (qa-requested)

Script executed:

# Show the diff for .gitignore to see the exact change
git show HEAD:.gitignore | grep -n -A 2 -B 2 "Agents.md"

Repository: AndyMik90/Auto-Claude


Repository: AndyMik90/Auto-Claude
Exit code: 0

stdout:

174-/shared_docs
175-logs/security/
176:Agents.md

Script executed:

# Search for any references to Agents.md in the codebase
rg "Agents\.md" --type-list

Repository: AndyMik90/Auto-Claude


Repository: AndyMik90/Auto-Claude
Exit code: 2

stderr:

error: The argument '<PATTERN>' cannot be used with '--type-list'

USAGE:
    
    rg [OPTIONS] PATTERN [PATH ...]
    rg [OPTIONS] -e PATTERN ... [PATH ...]
    rg [OPTIONS] -f PATTERNFILE ... [PATH ...]
    rg [OPTIONS] --files [PATH ...]
    rg [OPTIONS] --type-list
    command | rg [OPTIONS] PATTERN
    rg [OPTIONS] --help
    rg [OPTIONS] --version

For more information try --help


Script executed:

# Check if Agents.md exists in the repository
find . -name "Agents.md" -type f 2>/dev/null

Repository: AndyMik90/Auto-Claude


Repository: AndyMik90/Auto-Claude
Exit code: 0


Script executed:

# Search for any references to "Agents" in the codebase
rg "Agents" --type-list

Repository: AndyMik90/Auto-Claude


Repository: AndyMik90/Auto-Claude
Exit code: 2

stderr:

error: The argument '<PATTERN>' cannot be used with '--type-list'

USAGE:
    
    rg [OPTIONS] PATTERN [PATH ...]
    rg [OPTIONS] -e PATTERN ... [PATH ...]
    rg [OPTIONS] -f PATTERNFILE ... [PATH ...]
    rg [OPTIONS] --files [PATH ...]
    rg [OPTIONS] --type-list
    command | rg [OPTIONS] PATTERN
    rg [OPTIONS] --help
    rg [OPTIONS] --version

For more information try --help


Script executed:

# Correct search for Agents references
rg "Agents"

Repository: AndyMik90/Auto-Claude


Repository: AndyMik90/Auto-Claude
Exit code: 0

stdout:

README.md:5. **Watch it work** - Agents plan, code, and validate autonomously
README.md:| **Memory Layer** | Agents retain insights across sessions for smarter builds |
README.md:![Agent Terminals](.github/assets/Auto-Claude-Agents-terminals.png)
apps/frontend/src/renderer/components/github-prs/components/PRLogs.tsx:  const [expandedAgents, setExpandedAgents] = useState<Set<string>>(new Set());
apps/frontend/src/renderer/components/github-prs/components/PRLogs.tsx:    setExpandedAgents(prev => {
apps/frontend/src/renderer/components/github-prs/components/PRLogs.tsx:                expandedAgents={expandedAgents}
apps/frontend/src/renderer/components/github-prs/components/PRLogs.tsx:  expandedAgents: Set<string>;
apps/frontend/src/renderer/components/github-prs/components/PRLogs.tsx:function PhaseLogSection({ phase, phaseLog, isExpanded, onToggle, isStreaming = false, expandedAgents, onToggleAgent }: PhaseLogSectionProps) {
apps/frontend/src/renderer/components/github-prs/components/PRLogs.tsx:              expandedAgents={expandedAgents}
apps/frontend/src/renderer/components/github-prs/components/PRLogs.tsx:  expandedAgents: Set<string>;
apps/frontend/src/renderer/components/github-prs/components/PRLogs.tsx:function GroupedLogEntries({ entries, phase, expandedAgents, onToggleAgent }: GroupedLogEntriesProps) {
apps/frontend/src/renderer/components/github-prs/components/PRLogs.tsx:          isExpanded={expandedAgents.has(`${phase}-orchestrator-activity`)}
apps/frontend/src/renderer/components/github-prs/components/PRLogs.tsx:          isExpanded={expandedAgents.has(`${phase}-${group.agentName}`)}
apps/backend/security/git_validators.py:    Agents should not set user.name, user.email, etc. as this:
apps/backend/runners/github/services/parallel_followup_reviewer.py:    Specialist Agents:
apps/backend/runners/github/services/parallel_followup_reviewer.py:                f"[ParallelFollowup] Session complete. Agents invoked: {final_agents}"
apps/backend/runners/github/services/parallel_followup_reviewer.py:                f"[ParallelFollowup] Complete. Agents invoked: {final_agents}",
apps/backend/runners/github/services/parallel_followup_reviewer.py:Agents invoked: {agents_str}
apps/backend/runners/github/services/parallel_orchestrator_reviewer.py:                f"[ParallelOrchestrator] Session complete. Agents invoked: {final_agents}"
apps/backend/runners/github/services/parallel_orchestrator_reviewer.py:                f"[ParallelOrchestrator] Complete. Agents invoked: {final_agents}",
apps/backend/runners/github/services/parallel_orchestrator_reviewer.py:        # Agents used
apps/backend/runners/github/services/parallel_orchestrator_reviewer.py:            lines.append(f"**Specialist Agents Invoked:** {', '.join(agents_invoked)}")
apps/backend/runners/github/services/sdk_utils.py:        self._write(f"Agents invoked: {summary.get('agents_invoked', [])}")
apps/backend/prompts/github/pr_followup_orchestrator.md:## Available Specialist Agents
apps/backend/prompts/github/pr_followup_orchestrator.md:### Phase 2: Delegate to Agents (USE TASK TOOL)
apps/backend/prompts/github/pr_parallel_orchestrator.md:## Available Specialist Agents
apps/backend/prompts/github/pr_parallel_orchestrator.md:## CRITICAL: How to Invoke Specialist Agents
apps/backend/prompts_pkg/prompt_generator.py:    # Agents no longer need to call Linear MCP tools directly
apps/backend/agents/tools_pkg/__init__.py:Custom MCP Tools for Auto-Claude Agents
apps/backend/agents/__init__.py:Agents Module
apps/backend/agents/README.md:# Agents Module

Script executed:

# Check git diff to see the actual change made to .gitignore in this PR
git diff HEAD~1 .gitignore

Repository: AndyMik90/Auto-Claude


Repository: AndyMik90/Auto-Claude
Exit code: 128

stderr:

fatal: ambiguous argument 'HEAD~1': unknown revision or path not in the working tree.
Use '--' to separate paths from revisions, like this:
'git <command> [<revision>...] -- [<file>...]'

Script executed:

# Check the git history more carefully - see commits affecting .gitignore
git log --oneline -- .gitignore | head -10

Repository: AndyMik90/Auto-Claude


Repository: AndyMik90/Auto-Claude
Exit code: 0

stdout:

1205444 fix: remove unrelated changes from branch (qa-requested)

Script executed:

# Get more context about the current .gitignore - check if Agents.md is a new addition
git diff --no-index /dev/null .gitignore 2>&1 | grep -A 5 -B 5 "Agents.md" || cat .gitignore | tail -20

Repository: AndyMik90/Auto-Claude


Repository: AndyMik90/Auto-Claude
Exit code: 0

stdout:

+
+# Auto Claude generated files
+.security-key
+/shared_docs
+logs/security/
+Agents.md
\ No newline at end of file

Script executed:

# Check what files are in the PR - look at all changes in this commit
git diff --name-only HEAD

Repository: AndyMik90/Auto-Claude


Repository: AndyMik90/Auto-Claude
Exit code: 0


Script executed:

# Check the commit message for more context
git log -1 --format=%B

Repository: AndyMik90/Auto-Claude


Repository: AndyMik90/Auto-Claude
Exit code: 0

stdout:

fix: remove unrelated changes from branch (qa-requested)

Reset files that were not related to the expand button feature back to
their develop branch state:
- .gitignore
- apps/backend/agents/ (base.py, coder.py, planner.py, session.py)
- apps/backend/core/ (client.py, simple_client.py)
- apps/frontend/src/renderer/App.tsx
- apps/frontend/src/renderer/components/AuthStatusIndicator.tsx
- apps/frontend/src/renderer/components/KanbanBoard.tsx
- apps/frontend/src/renderer/stores/task-store.ts
- apps/frontend/src/shared/i18n/locales/*/common.json
- tests/test_auth.py
- tests/test_issue_884_plan_schema.py

The expand button feature implementation remains intact.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>


Contradiction: commit claims to reset .gitignore but actually adds new entries.

The commit message states that .gitignore was reset to remove unrelated changes from the branch, yet the diff shows new entries being added (Agents.md, .security-key, /shared_docs, logs/security/) under a "# Auto Claude generated files" section. Since Agents.md does not exist in the repository and is unrelated to the expand button feature, clarify whether these additions are intentional or should be reverted to match the commit's stated objective of removing unrelated changes.

🤖 Prompt for AI Agents
In @.gitignore at line 176, The .gitignore shows new entries under the "# Auto
Claude generated files" section (e.g., Agents.md, .security-key, /shared_docs,
logs/security/) that contradict the commit message claiming a reset; either
remove these added lines to truly revert unrelated changes or explicitly
document and justify them in the commit message. Locate the "# Auto Claude
generated files" block and delete the specific entries (Agents.md,
.security-key, /shared_docs, logs/security/) if they were unintended, or update
the commit message to explain why these ignores are required and ensure any
referenced files exist or are intentionally omitted.

7 changes: 0 additions & 7 deletions apps/backend/agents/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,3 @@
# Configuration constants
AUTO_CONTINUE_DELAY_SECONDS = 3
HUMAN_INTERVENTION_FILE = "PAUSE"

# Retry configuration for 400 tool concurrency errors
MAX_CONCURRENCY_RETRIES = 5 # Maximum number of retries for tool concurrency errors
INITIAL_RETRY_DELAY_SECONDS = (
2 # Initial retry delay (doubles each retry: 2s, 4s, 8s, 16s, 32s)
)
MAX_RETRY_DELAY_SECONDS = 32 # Cap retry delay at 32 seconds
143 changes: 6 additions & 137 deletions apps/backend/agents/coder.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,13 +56,7 @@
print_status,
)

from .base import (
AUTO_CONTINUE_DELAY_SECONDS,
HUMAN_INTERVENTION_FILE,
INITIAL_RETRY_DELAY_SECONDS,
MAX_CONCURRENCY_RETRIES,
MAX_RETRY_DELAY_SECONDS,
)
from .base import AUTO_CONTINUE_DELAY_SECONDS, HUMAN_INTERVENTION_FILE
from .memory_manager import debug_memory_system_status, get_graphiti_context
from .session import post_session_processing, run_agent_session
from .utils import (
Expand Down Expand Up @@ -221,21 +215,6 @@ def _validate_and_fix_implementation_plan() -> tuple[bool, list[str]]:

# Main loop
iteration = 0
consecutive_concurrency_errors = 0 # Track consecutive 400 tool concurrency errors
current_retry_delay = INITIAL_RETRY_DELAY_SECONDS # Exponential backoff delay
concurrency_error_context: str | None = (
None # Context to pass to agent after concurrency error
)

def _reset_concurrency_state() -> None:
"""Reset concurrency error tracking state after a successful session or non-concurrency error."""
nonlocal \
consecutive_concurrency_errors, \
current_retry_delay, \
concurrency_error_context
consecutive_concurrency_errors = 0
current_retry_delay = INITIAL_RETRY_DELAY_SECONDS
concurrency_error_context = None

while True:
iteration += 1
Expand Down Expand Up @@ -426,14 +405,6 @@ def _reset_concurrency_state() -> None:
prompt += "\n\n" + graphiti_context
print_status("Graphiti memory context loaded", "success")

# Add concurrency error context if recovering from 400 error
if concurrency_error_context:
prompt += "\n\n" + concurrency_error_context
print_status(
f"Added tool concurrency error context (retry {consecutive_concurrency_errors}/{MAX_CONCURRENCY_RETRIES})",
"warning",
)

# Show what we're working on
print(f"Working on: {highlight(subtask_id)}")
print(f"Description: {next_subtask.get('description', 'No description')}")
Expand All @@ -448,7 +419,7 @@ def _reset_concurrency_state() -> None:

# Run session with async context manager
async with client:
status, response, error_info = await run_agent_session(
status, response = await run_agent_session(
client, prompt, spec_dir, verbose, phase=current_log_phase
)

Expand Down Expand Up @@ -541,9 +512,6 @@ def _reset_concurrency_state() -> None:
print_build_complete_banner(spec_dir)
status_manager.update(state=BuildState.COMPLETE)

# Reset error tracking on success
_reset_concurrency_state()

if task_logger:
task_logger.end_phase(
LogPhase.CODING,
Expand All @@ -558,9 +526,6 @@ def _reset_concurrency_state() -> None:
break

elif status == "continue":
# Reset error tracking on successful session
_reset_concurrency_state()

print(
muted(
f"\nAgent will auto-continue in {AUTO_CONTINUE_DELAY_SECONDS}s..."
Expand Down Expand Up @@ -591,106 +556,10 @@ def _reset_concurrency_state() -> None:

elif status == "error":
emit_phase(ExecutionPhase.FAILED, "Session encountered an error")

# Check if this is a tool concurrency error (400)
is_concurrency_error = (
error_info and error_info.get("type") == "tool_concurrency"
)

if is_concurrency_error:
consecutive_concurrency_errors += 1

# Check if we've exceeded max retries (allow 5 retries with delays: 2s, 4s, 8s, 16s, 32s)
if consecutive_concurrency_errors > MAX_CONCURRENCY_RETRIES:
print_status(
f"Tool concurrency limit hit {consecutive_concurrency_errors} times consecutively",
"error",
)
print()
print("=" * 70)
print(" CRITICAL: Agent stuck in retry loop")
print("=" * 70)
print()
print(
"The agent is repeatedly hitting Claude API's tool concurrency limit."
)
print(
"This usually means the agent is trying to use too many tools at once."
)
print()
print("Possible solutions:")
print(" 1. The agent needs to reduce tool usage per request")
print(" 2. Break down the current subtask into smaller steps")
print(" 3. Manual intervention may be required")
print()
print(f"Error: {error_info.get('message', 'Unknown error')[:200]}")
print()

# Mark current subtask as stuck if we have one
if subtask_id:
recovery_manager.mark_subtask_stuck(
subtask_id,
f"Tool concurrency errors after {consecutive_concurrency_errors} retries",
)
print_status(f"Subtask {subtask_id} marked as STUCK", "error")

status_manager.update(state=BuildState.ERROR)
break # Exit the loop

# Exponential backoff: 2s, 4s, 8s, 16s, 32s
print_status(
f"Tool concurrency error (retry {consecutive_concurrency_errors}/{MAX_CONCURRENCY_RETRIES})",
"warning",
)
print(
muted(
f"Waiting {current_retry_delay}s before retry (exponential backoff)..."
)
)
print()

# Set context for next retry so agent knows to adjust behavior
error_context_message = (
"## CRITICAL: TOOL CONCURRENCY ERROR\n\n"
f"Your previous session hit Claude API's tool concurrency limit (HTTP 400).\n"
f"This is retry {consecutive_concurrency_errors}/{MAX_CONCURRENCY_RETRIES}.\n\n"
"**IMPORTANT: You MUST adjust your approach:**\n"
"1. Use ONE tool at a time - do NOT call multiple tools in parallel\n"
"2. Wait for each tool result before calling the next tool\n"
"3. Avoid starting with `pwd` or multiple Read calls at once\n"
"4. If you need to read multiple files, read them one by one\n"
"5. Take a more incremental, step-by-step approach\n\n"
"Start by focusing on ONE specific action for this subtask."
)

# If we're in planning phase, reset first_run to True so next iteration
# re-enters the planning branch (fix for issue #1565)
if current_log_phase == LogPhase.PLANNING:
first_run = True
planning_retry_context = error_context_message
print_status(
"Planning session failed - will retry planning", "warning"
)
else:
concurrency_error_context = error_context_message

status_manager.update(state=BuildState.ERROR)
await asyncio.sleep(current_retry_delay)

# Double the retry delay for next time (cap at MAX_RETRY_DELAY_SECONDS)
current_retry_delay = min(
current_retry_delay * 2, MAX_RETRY_DELAY_SECONDS
)

else:
# Other errors - use standard retry logic
print_status("Session encountered an error", "error")
print(muted("Will retry with a fresh session..."))
status_manager.update(state=BuildState.ERROR)
await asyncio.sleep(AUTO_CONTINUE_DELAY_SECONDS)

# Reset concurrency error tracking on non-concurrency errors
_reset_concurrency_state()
print_status("Session encountered an error", "error")
print(muted("Will retry with a fresh session..."))
status_manager.update(state=BuildState.ERROR)
await asyncio.sleep(AUTO_CONTINUE_DELAY_SECONDS)

# Small delay between sessions
if max_iterations is None or iteration < max_iterations:
Expand Down
2 changes: 1 addition & 1 deletion apps/backend/agents/planner.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ async def run_followup_planner(
try:
# Run single planning session
async with client:
status, response, error_info = await run_agent_session(
status, response = await run_agent_session(
client, prompt, spec_dir, verbose, phase=LogPhase.PLANNING
)

Expand Down
62 changes: 9 additions & 53 deletions apps/backend/agents/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,28 +46,6 @@
logger = logging.getLogger(__name__)


def is_tool_concurrency_error(error: Exception) -> bool:
"""
Check if an error is a 400 tool concurrency error from Claude API.

Tool concurrency errors occur when too many tools are used simultaneously
in a single API request, hitting Claude's concurrent tool use limit.

Args:
error: The exception to check

Returns:
True if this is a tool concurrency error, False otherwise
"""
error_str = str(error).lower()
# Check for 400 status AND tool concurrency keywords
return "400" in error_str and (
("tool" in error_str and "concurrency" in error_str)
or "too many tools" in error_str
or "concurrent tool" in error_str
)


async def post_session_processing(
spec_dir: Path,
project_dir: Path,
Expand Down Expand Up @@ -339,7 +317,7 @@ async def run_agent_session(
spec_dir: Path,
verbose: bool = False,
phase: LogPhase = LogPhase.CODING,
) -> tuple[str, str, dict]:
) -> tuple[str, str]:
"""
Run a single agent session using Claude Agent SDK.

Expand All @@ -351,13 +329,10 @@ async def run_agent_session(
phase: Current execution phase for logging

Returns:
(status, response_text, error_info) where:
- status: "continue", "complete", or "error"
- response_text: Agent's response text
- error_info: Dict with error details (empty if no error):
- "type": "tool_concurrency" or "other"
- "message": Error message string
- "exception_type": Exception class name string
(status, response_text) where status is:
- "continue" if agent should continue working
- "complete" if all subtasks complete
- "error" if an error occurred
"""
debug_section("session", f"Agent Session - {phase.value}")
debug(
Expand Down Expand Up @@ -554,7 +529,7 @@ async def run_agent_session(
tool_count=tool_count,
response_length=len(response_text),
)
return "complete", response_text, {}
return "complete", response_text

debug_success(
"session",
Expand All @@ -563,36 +538,17 @@ async def run_agent_session(
tool_count=tool_count,
response_length=len(response_text),
)
return "continue", response_text, {}
return "continue", response_text

except Exception as e:
# Detect specific error types for better retry handling
is_concurrency = is_tool_concurrency_error(e)
error_type = "tool_concurrency" if is_concurrency else "other"

debug_error(
"session",
f"Session error: {e}",
exception_type=type(e).__name__,
error_category=error_type,
message_count=message_count,
tool_count=tool_count,
)

# Log concurrency errors prominently
if is_concurrency:
print("\n⚠️ Tool concurrency limit reached (400 error)")
print(" Claude API limits concurrent tool use in a single request")
print(f" Error: {str(e)[:200]}\n")
else:
print(f"Error during agent session: {e}")

print(f"Error during agent session: {e}")
if task_logger:
task_logger.log_error(f"Session error: {e}", phase)

error_info = {
"type": error_type,
"message": str(e),
"exception_type": type(e).__name__,
}
return "error", str(e), error_info
return "error", str(e)
17 changes: 4 additions & 13 deletions apps/backend/core/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -490,28 +490,19 @@ def create_client(
(see security.py for ALLOWED_COMMANDS)
4. Tool filtering - Each agent type only sees relevant tools (prevents misuse)
"""
# Collect env vars to pass to SDK (ANTHROPIC_BASE_URL, CLAUDE_CONFIG_DIR, etc.)
sdk_env = get_sdk_env_vars()

# Get the config dir for profile-specific credential lookup
# CLAUDE_CONFIG_DIR enables per-profile Keychain entries with SHA256-hashed service names
config_dir = sdk_env.get("CLAUDE_CONFIG_DIR")

# Get OAuth token - uses profile-specific Keychain lookup when config_dir is set
# This correctly reads from "Claude Code-credentials-{hash}" for non-default profiles
oauth_token = require_auth_token(config_dir)
# Get OAuth token - Claude CLI handles token lifecycle internally
oauth_token = require_auth_token()

# Validate token is not encrypted before passing to SDK
# Encrypted tokens (enc:...) should have been decrypted by require_auth_token()
# If we still have an encrypted token here, it means decryption failed or was skipped
validate_token_not_encrypted(oauth_token)

# Ensure SDK can access it via its expected env var
# This is required because the SDK doesn't know about per-profile Keychain naming
os.environ["CLAUDE_CODE_OAUTH_TOKEN"] = oauth_token

if config_dir:
logger.info(f"Using CLAUDE_CONFIG_DIR for profile: {config_dir}")
# Collect env vars to pass to SDK (ANTHROPIC_BASE_URL, etc.)
sdk_env = get_sdk_env_vars()

# Debug: Log git-bash path detection on Windows
if "CLAUDE_CODE_GIT_BASH_PATH" in sdk_env:
Expand Down
20 changes: 7 additions & 13 deletions apps/backend/core/simple_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
"""

import logging
import os
from pathlib import Path

from agents.tools_pkg import get_agent_config, get_default_thinking_level
Expand Down Expand Up @@ -73,26 +72,21 @@ def create_simple_client(
Raises:
ValueError: If agent_type is not found in AGENT_CONFIGS
"""
# Get environment variables for SDK (including CLAUDE_CONFIG_DIR if set)
sdk_env = get_sdk_env_vars()

# Get the config dir for profile-specific credential lookup
# CLAUDE_CONFIG_DIR enables per-profile Keychain entries with SHA256-hashed service names
config_dir = sdk_env.get("CLAUDE_CONFIG_DIR")

# Get OAuth token - uses profile-specific Keychain lookup when config_dir is set
# This correctly reads from "Claude Code-credentials-{hash}" for non-default profiles
oauth_token = require_auth_token(config_dir)
# Get authentication
oauth_token = require_auth_token()

# Validate token is not encrypted before passing to SDK
# Encrypted tokens (enc:...) should have been decrypted by require_auth_token()
# If we still have an encrypted token here, it means decryption failed or was skipped
validate_token_not_encrypted(oauth_token)

# Ensure SDK can access it via its expected env var
# This is required because the SDK doesn't know about per-profile Keychain naming
import os
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

According to PEP 8, imports should typically be at the top of the file. Moving import os to the top-level imports would improve code style consistency. It seems it was removed from the top in this same PR.


os.environ["CLAUDE_CODE_OAUTH_TOKEN"] = oauth_token

# Get environment variables for SDK
sdk_env = get_sdk_env_vars()

# Get agent configuration (raises ValueError if unknown type)
config = get_agent_config(agent_type)

Expand Down
Loading