Remote Execution and Scheduled Triggers: The Headless AI Agent
A deep dive into Claude Code's remote capabilities — remote sessions, Server mode, Cron scheduled tasks, RemoteTrigger, and SDK headless mode
The Problem
You're on a development machine using Claude Code to fix a bug, and suddenly you need to step out. You don't want to interrupt the current task — can Claude continue working in the cloud? The next morning, you also want Claude to automatically check PR status and report back daily. This isn't science fiction — it's the core capability of Claude Code's remote execution system.
Traditional CLI tools are bound to terminal sessions — close the terminal, and the process dies. Claude Code breaks this limitation by introducing remote capabilities across three dimensions:
- Remote Sessions — Run Claude in a cloud environment, with the local terminal acting only as a proxy
- Server Mode (Direct Connect) — Connect to a remote Claude instance via WebSocket
- Scheduled Triggers (Cron + RemoteTrigger) — Let Claude execute tasks automatically on a schedule
This article provides a deep analysis of the design and implementation across all three dimensions.
Remote Session Architecture Overview
Remote Session Preconditions
Remote sessions aren't unconditionally available. The system performs a series of eligibility checks before creating a remote session to ensure the environment meets requirements. These checks are defined in src/utils/background/remote/preconditions.ts:
The precondition checks use a parallel execution strategy, coordinated in remoteSession.ts:
This code demonstrates an important design pattern — parallel precondition checks. Three independent checks fire simultaneously:
- Login status — Whether the OAuth token is valid
- Remote environment — Whether the user has an available cloud environment
- Repository detection — Whether the current directory is within a Git repository, along with remote information
Bundle Seed Mechanism
An interesting optimization is the Bundle Seed mechanism. When enabled (via the CCR_FORCE_BUNDLE or CCR_ENABLE_BUNDLE environment variables), the system only needs a local .git/ directory — no GitHub remote and no GitHub App required:
This means for local repositories (a git init repo without a GitHub remote), Bundle Seed can package and upload local code to the cloud instead of requiring CCR (Claude Code Remote) to pull from GitHub.
Repository Access Layers
For scenarios that require pulling code from GitHub, the system implements layered access checking:
Three priority levels:
- GitHub App — The preferred method, authorized through a GitHub App installed on the repository
- Token Sync — A GitHub token synced via
/web-setup(gated by a feature flag) - None — The user needs to configure an access method
Remote Session Type System
Remote sessions have explicit type definitions and a state machine:
Sessions record a complete message log via SDKMessage[], meaning that even if the local connection drops, the full execution history can be restored upon reconnecting.
Direct Connect: Server Mode
Server mode is an alternative remote execution method — rather than creating a new environment via Teleport in the cloud, it connects to a server already running Claude Code. This is particularly useful in enterprise intranet scenarios.
Session Creation
The creation process sends a POST request to ${serverUrl}/sessions, and the returned DirectConnectConfig contains key information:
WebSocket Bidirectional Communication
DirectConnectSessionManager encapsulates the complete WebSocket communication protocol:
Message handling splits into three channels:
- SDK messages — Standard messages like assistant/result/system, forwarded to the local UI
- Permission requests —
control_requesttype messages requiring local user confirmation - Filtered messages — Internal messages like keep_alive and streamlined_text, not forwarded
Permission Requests and Interrupts
Permission handling during remote execution is particularly critical. When the remote Agent needs to perform a dangerous operation, the permission request is sent to the local machine via WebSocket. After the user makes a decision, the result is sent back:
The interrupt mechanism also works through WebSocket:
Cron Scheduled Task System
Claude Code includes a complete Cron scheduling system that lets the AI Agent execute tasks automatically on a schedule.
Task Storage
Tasks are stored in two ways:
| Type | Storage Location | Lifecycle | Use Case |
|---|---|---|---|
| Durable | .claude/scheduled_tasks.json | Persists across sessions | Created by users via CronCreateTool |
| Session-only | In-process memory (bootstrap/state) | Dies with the process | Temporary tasks created by sub-agents |
Scheduler Lock
When multiple Claude sessions run in the same project directory, only one should drive the Cron scheduler. The system uses file locks for coordination:
The lock design has several subtle features:
- O_EXCL atomic creation — Uses the
'wx'flag to ensure lock file creation is atomic - PID liveness detection — Uses
isProcessRunning()to check whether the lock-holding process is still alive - Stale lock recovery — If the lock-holding process has died, deletes the lock file and retries
- Idempotent reacquisition — If the session ID matches (PID changed after
--resume), updates the PID
Jitter to Prevent Thundering Herd
When many users set the same cron expression (e.g., 0 * * * *, every hour on the hour), all tasks fire simultaneously, causing inference service load spikes. The system uses a Jitter mechanism to spread out trigger times.
Forward jitter for recurring tasks:
Jitter is calculated based on a hash of the task ID, ensuring determinism (the same task always gets the same delay) and uniform distribution. With default configuration, recurring tasks with a one-hour interval are spread between :00 and :06.
Backward jitter for one-shot tasks:
One-shot tasks (like "remind me at 3 PM") can't be delayed — that would violate user expectations. But firing slightly early is acceptable:
Jitter is only applied at the top and bottom of the hour (:00 and :30) — because humans tend to choose these "round" times. The default maximum is 90 seconds early.
Jitter Configuration as Operational Knobs
These configurations can be remotely adjusted via GrowthBook's tengu_kairos_cron_config. When the inference service experiences capacity issues, operations can push more aggressive configurations — for example, changing oneShotMinuteMod to 15 (covering :00/:15/:30/:45) and oneShotMaxMs to 300000 (5 minutes) to significantly spread the load.
Recurring Task Auto-Expiry
Recurring tasks have a default 7-day lifetime, preventing forgotten tasks from consuming resources indefinitely:
Only tasks marked as permanent (such as the built-in catch-up/morning-checkin/dream tasks in assistant mode) are exempt from expiry.
RemoteTrigger Tool
RemoteTriggerTool is another remote execution path — instead of using the local scheduler, it directly calls the claude.ai API to manage remote triggers:
The core security principle is that the OAuth token is never exposed in the shell. The tool adds the authentication header directly in-process, preventing the token from appearing in command-line arguments or environment variables where it could be captured by other processes or log systems.
SDK Headless Mode
The lowest layer of remote execution is the SDK's headless mode. When launched with --input-format stream-json, Claude Code doesn't start a terminal UI — instead, it communicates via JSON streams over stdin/stdout.
The Direct Connect message format must match SDK expectations:
This format is fully consistent with SDKUserMessage, ensuring unified message handling whether the instance is a local REPL or a remote headless instance.
Security Model
Remote execution introduces additional security considerations:
- Policy control —
isPolicyAllowed('allow_remote_sessions')intercepts at the outermost layer - OAuth authentication — Ensures the user's identity is legitimate
- Repository access — Layered checks for GitHub App or Token Sync
- Permission proxying — The remote Agent's tool usage requires local user confirmation via WebSocket
- Environment isolation — Each remote session runs in an independent environment
The dangerouslySkipPermissions option is intended only for controlled environments (such as CI/CD). It bypasses permission interactions but does not bypass security policies.
Complete Data Flow
The data flow for a complete remote execution request is as follows:
- The user initiates a remote task locally
- The system checks preconditions in parallel (login, environment, repository)
- A connection is established via Teleport/Direct Connect
- The remote Agent receives the prompt and begins execution
- Tool permission requests are sent back to the local machine via WebSocket
- After user confirmation, the permission response is sent back to the remote
- Execution results stream back via SDK messages
- The task completes and status updates to
completed
For Cron tasks, the trigger flow is slightly different:
- The scheduler acquires the lock (ensuring single-instance execution)
- Checks
.claude/scheduled_tasks.jsonfor due tasks - Calculates the actual trigger time with jitter applied
- Enqueues the prompt into the message queue
- The main REPL loop (or sub-agent) processes tasks from the queue
- Recurring tasks update
lastFiredAtand reschedule
Summary
Claude Code's remote execution system demonstrates how to extend a terminal tool into a distributed AI Agent platform. The core design principles include:
- Multi-path remote execution — Teleport (new cloud environment), Direct Connect (connect to existing server), and RemoteTrigger (API-triggered) are three independent paths covering different scenarios
- Deterministic jitter — Hash-based deterministic delays tied to task IDs prevent thundering herd effects while maintaining predictability
- File lock coordination — O_EXCL atomic creation + PID liveness detection resolves multi-session scheduler contention
- Layered security — Policy, authentication, permissions, and isolation form four layers of protection, ensuring remote execution doesn't weaken security guarantees
- Operational controllability — Jitter configuration and task expiry policies can be adjusted in real time via remote configuration
These designs weren't built in a vacuum — they solve the engineering challenges that inevitably arise when an AI Agent evolves from a single-machine tool into a distributed system.