Extracted from
versions/2.1.52/cli.js(Claude Code v2.1.52, built 2026-02-24) Purpose: Enable a local CLI agent session to be accessed/controlled from a web UI (claude.ai/code)
- Architecture Overview
- Feature Gating
- Entry Points
- API Client
- Standalone Bridge Flow
- Main Poll Loop
- Session Spawner
- In-REPL Bridge
- Transport Layer (HybridTransport)
- State Management
- Session Activity Tracking
- Status Display
- Error Handling
- Security Model
- Constants & Configuration
┌─────────────────────────────────────────────────────────────┐
│ claude.ai/code (Web UI) │
└──────────────────────────┬──────────────────────────────────┘
│ HTTPS / WSS
▼
┌─────────────────────────────────────────────────────────────┐
│ Anthropic Environments API │
│ /v1/environments/bridge (register/deregister) │
│ /v1/environments/{id}/work/poll (long-poll for work) │
│ /v1/sessions/{id}/events (send events) │
└──────────────────────────┬──────────────────────────────────┘
│
┌────────────┴────────────┐
│ │
┌──────────▼──────────┐ ┌──────────▼──────────┐
│ Standalone Bridge │ │ In-REPL Bridge │
│ `claude remote- │ │ `/remote-control` │
│ control` │ │ slash command │
│ │ │ │
│ Spawns child claude │ │ Runs in-process, │
│ processes for each │ │ shares the existing │
│ session │ │ REPL session │
└──────────────────────┘ └──────────────────────┘
Two operation modes:
- Standalone (
claude remote-control): A dedicated bridge process that spawns childclaudeprocesses for each remote session - In-REPL (
/remote-control): Runs within an existing interactiveclaudesession, making it remotely accessible
// cli.js:456447
function isBridgeEnabled() {
// Checks the "tengu_ccr_bridge" feature flag, defaults to false
return getFeatureFlag("tengu_ccr_bridge", false);
}// cli.js:511920
function isRemoteControlEnabled() {
return isBridgeEnabled() && isPolicyAllowed("allow_remote_sessions");
}// cli.js:511600-511612
var BRIDGE_MAX_LIFETIME_MS = 86400000; // 24 hours
var DEFAULT_SESSION_TIMEOUT_MS = 86400000; // 24 hours
var BRIDGE_LOGIN_INSTRUCTION =
"Remote Control is only available with claude.ai subscriptions. " +
"Please use `/login` to sign in with your claude.ai account.";
var BRIDGE_LOGIN_ERROR =
"Error: You must be logged in to use Remote Control.\n\n" +
"Remote Control is only available with claude.ai subscriptions. " +
"Please use `/login` to sign in with your claude.ai account.";// cli.js:592883-592915
// Accepts multiple aliases for the subcommand
if (
args[0] === "remote-control" ||
args[0] === "rc" ||
args[0] === "remote" ||
args[0] === "sync" ||
args[0] === "bridge"
) {
trackEvent("cli_bridge_path");
// Load and validate configs
const { enableConfigs } = await import("configs");
enableConfigs();
const { isBridgeEnabled } = await import("bridge-flags");
const { BRIDGE_LOGIN_ERROR } = await import("bridge-constants");
const { bridgeMain } = await import("bridge-main");
const { getClaudeAIOAuthTokens } = await import("oauth");
// Must be logged in
if (!getClaudeAIOAuthTokens()?.accessToken) {
console.error(BRIDGE_LOGIN_ERROR);
process.exit(1);
}
// Must have feature flag
if (!isBridgeEnabled()) {
console.error(
"Error: Remote Control is not enabled for your account. Contact your administrator."
);
process.exit(1);
}
// Must pass org policy
const { waitForPolicyLimitsToLoad, isPolicyAllowed } = await import("policy");
await waitForPolicyLimitsToLoad();
if (!isPolicyAllowed("allow_remote_sessions")) {
console.error(
"Error: Remote Control sessions are disabled by your organization's policy."
);
process.exit(1);
}
await bridgeMain(args.slice(1));
return;
}// cli.js:511924-511943
var remoteControlCommand = {
type: "local-jsx",
name: "remote-control",
aliases: ["rc"],
description: "Connect this terminal for remote-control sessions",
isEnabled: isRemoteControlEnabled,
get isHidden() {
return !isRemoteControlEnabled();
},
immediate: true, // executes immediately, no further prompt needed
load: () => import("./remote-control-call"),
userFacingName() {
return "remote-control";
},
};// cli.js:590557-590558
// When running as a bridge child process, set the entrypoint type
if (process.env.CLAUDE_CODE_ENVIRONMENT_KIND === "bridge") {
setEntrypoint("remote-control");
}// cli.js:535875-536068
function createBridgeApiClient({ baseUrl, getAccessToken, runnerVersion, onDebug }) {
function log(msg) {
onDebug?.(msg);
}
let emptyPollCount = 0;
const LOG_EVERY_N_POLLS = 100;
function makeHeaders(accessToken) {
return {
Authorization: `Bearer ${accessToken}`,
"Content-Type": "application/json",
"anthropic-version": "2023-06-01",
"anthropic-beta": "environments-2025-11-01",
"x-environment-runner-version": runnerVersion,
};
}
function requireToken() {
const token = getAccessToken();
if (!token) throw Error(BRIDGE_LOGIN_INSTRUCTION);
return token;
}
// Auto-refresh on 401
async function withTokenRefresh(requestFn, label) {
let token = requireToken();
let response = await requestFn(token);
if (response.status !== 401) return response;
log(`[bridge:api] ${label}: 401 received, attempting token refresh`);
if (await refreshOAuthToken(token)) {
log(`[bridge:api] ${label}: Token refreshed, retrying request`);
const newToken = requireToken();
const retryResponse = await requestFn(newToken);
if (retryResponse.status !== 401) return retryResponse;
log(`[bridge:api] ${label}: Retry after refresh also got 401`);
} else {
log(`[bridge:api] ${label}: Token refresh failed`);
}
return response;
}
return {
// Register this bridge environment with the server
async registerBridgeEnvironment(config) {
log(`[bridge:api] POST /v1/environments/bridge bridgeId=${config.bridgeId}`);
const response = await withTokenRefresh(
(token) =>
axios.post(
`${baseUrl}/v1/environments/bridge`,
{
machine_name: config.machineName,
directory: config.dir,
branch: config.branch,
git_repo_url: config.gitRepoUrl,
},
{ headers: makeHeaders(token), timeout: 15000, validateStatus: (s) => s < 500 }
),
"Registration"
);
validateResponse(response.status, response.data, "Registration");
return response.data; // { environment_id, environment_secret }
},
// Long-poll for work items (sessions to handle)
async pollForWork(environmentId, environmentSecret, signal) {
validateParam(environmentId, "environmentId");
let prevCount = emptyPollCount;
emptyPollCount = 0;
const response = await axios.get(
`${baseUrl}/v1/environments/${environmentId}/work/poll`,
{
headers: makeHeaders(environmentSecret),
params: { block_ms: 900, ack: true },
timeout: 10000,
signal,
validateStatus: (s) => s < 500,
}
);
validateResponse(response.status, response.data, "Poll");
if (!response.data) {
emptyPollCount = prevCount + 1;
if (emptyPollCount === 1 || emptyPollCount % LOG_EVERY_N_POLLS === 0) {
log(`[bridge:api] GET .../work/poll -> ${response.status} (no work, ${emptyPollCount} consecutive empty polls)`);
}
return null;
}
log(`[bridge:api] GET .../work/poll -> ${response.status} workId=${response.data.id} type=${response.data.data?.type}`);
return response.data;
},
// Acknowledge receipt of work
async acknowledgeWork(environmentId, workId, token) {
validateParam(environmentId, "environmentId");
validateParam(workId, "workId");
const response = await axios.post(
`${baseUrl}/v1/environments/${environmentId}/work/${workId}/ack`,
{},
{ headers: makeHeaders(token), timeout: 10000, validateStatus: (s) => s < 500 }
);
validateResponse(response.status, response.data, "Acknowledge");
},
// Stop a work item
async stopWork(environmentId, workId, force) {
validateParam(environmentId, "environmentId");
validateParam(workId, "workId");
const response = await withTokenRefresh(
(token) =>
axios.post(
`${baseUrl}/v1/environments/${environmentId}/work/${workId}/stop`,
{ force },
{ headers: makeHeaders(token), timeout: 10000, validateStatus: (s) => s < 500 }
),
"StopWork"
);
validateResponse(response.status, response.data, "StopWork");
},
// Deregister environment on shutdown
async deregisterEnvironment(environmentId) {
validateParam(environmentId, "environmentId");
const response = await withTokenRefresh(
(token) =>
axios.delete(`${baseUrl}/v1/environments/bridge/${environmentId}`, {
headers: makeHeaders(token),
timeout: 10000,
validateStatus: (s) => s < 500,
}),
"Deregister"
);
validateResponse(response.status, response.data, "Deregister");
},
// Retrieve session details (e.g., to get the title)
async getSession(sessionId) {
validateParam(sessionId, "sessionId");
const response = await withTokenRefresh(
(token) =>
axios.get(`${baseUrl}/v1/sessions/${sessionId}`, {
headers: makeHeaders(token),
timeout: 10000,
validateStatus: (s) => s < 500,
}),
"GetSession"
);
validateResponse(response.status, response.data, "GetSession");
return response.data;
},
// Archive a completed session
async archiveSession(sessionId) {
validateParam(sessionId, "sessionId");
const response = await withTokenRefresh(
(token) =>
axios.post(
`${baseUrl}/v1/sessions/${sessionId}/archive`,
{},
{ headers: makeHeaders(token), timeout: 10000, validateStatus: (s) => s < 500 }
),
"ArchiveSession"
);
if (response.status === 409) return; // already archived
validateResponse(response.status, response.data, "ArchiveSession");
},
// Send permission response events back to a session
async sendPermissionResponseEvent(sessionId, event, token) {
validateParam(sessionId, "sessionId");
const response = await axios.post(
`${baseUrl}/v1/sessions/${sessionId}/events`,
{ events: [event] },
{
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
"anthropic-version": "2023-06-01",
"anthropic-beta": "environments-2025-11-01",
},
timeout: 10000,
validateStatus: (s) => s < 500,
}
);
validateResponse(response.status, response.data, "SendPermissionResponseEvent");
},
};
}// cli.js:536070-536157
function validateResponse(status, data, label) {
if (status === 200) return;
const message = extractMessage(data);
const errorType = extractErrorType(data);
switch (status) {
case 401:
throw new BridgeFatalError(
`${label}: Authentication failed (401)${message ? `: ${message}` : ""}. ${BRIDGE_LOGIN_INSTRUCTION}`,
401, errorType
);
case 403:
throw new BridgeFatalError(
isExpiredError(errorType)
? "Remote Control session has expired. Please restart with `claude remote-control` or /remote-control."
: `${label}: Access denied (403)${message ? `: ${message}` : ""}. Check your organization permissions.`,
403, errorType
);
case 404:
throw new BridgeFatalError(
message ?? `${label}: Not found (404). Remote Control may not be available for this organization.`,
404, errorType
);
case 410:
throw new BridgeFatalError(
message ?? "Remote Control session has expired. Please restart with `claude remote-control` or /remote-control.",
410, errorType ?? "environment_expired"
);
case 429:
throw Error(`${label}: Rate limited (429). Polling too frequently.`);
default:
throw Error(`${label}: Failed with status ${status}${message ? `: ${message}` : ""}`);
}
}
function isExpiredError(errorType) {
if (!errorType) return false;
return errorType.includes("expired") || errorType.includes("lifetime");
}
function extractErrorType(data) {
if (data && typeof data === "object") {
if ("error" in data && data.error && typeof data.error === "object" &&
"type" in data.error && typeof data.error.type === "string")
return data.error.type;
}
return undefined;
}
function extractMessage(data) {
if (data && typeof data === "object") {
if ("message" in data && typeof data.message === "string") return data.message;
if ("error" in data && data.error && typeof data.error === "object" &&
"message" in data.error && typeof data.error.message === "string")
return data.error.message;
}
return undefined;
}
class BridgeFatalError extends Error {
status;
errorType;
constructor(message, status, errorType) {
super(message);
this.name = "BridgeFatalError";
this.status = status;
this.errorType = errorType;
}
}// cli.js:537500-537789 (simplified / reconstructed)
function parseBridgeArgs(args) {
let verbose = false, sandbox = false, debugFile, sessionTimeoutMs, help = false;
for (let i = 0; i < args.length; i++) {
const arg = args[i];
if (arg === "--help" || arg === "-h") help = true;
else if (arg === "--verbose" || arg === "-v") verbose = true;
else if (arg === "--sandbox") sandbox = true;
else if (arg === "--no-sandbox") sandbox = false;
else if (arg === "--debug-file" && i + 1 < args.length) debugFile = resolve(args[++i]);
else if (arg.startsWith("--debug-file=")) debugFile = resolve(arg.slice(13));
else if (arg === "--session-timeout" && i + 1 < args.length)
sessionTimeoutMs = parseInt(args[++i], 10) * 1000;
else if (arg.startsWith("--session-timeout="))
sessionTimeoutMs = parseInt(arg.slice(18), 10) * 1000;
}
return { verbose, sandbox, debugFile, sessionTimeoutMs, help };
}
function printHelp() {
console.log(`
Remote Control - Connect your local environment to claude.ai/code
USAGE
claude remote-control
DESCRIPTION
Remote Control allows you to control sessions on your local device from
claude.ai/code (https://claude.ai/code). Run this command in the
directory you want to work in, then connect from the Claude app or web.
NOTES
- You must be logged in with a Claude account that has a subscription
- Run \`claude\` first in the directory to accept the workspace trust dialog
`);
}
async function bridgeMain(args) {
const { verbose, sandbox, debugFile, sessionTimeoutMs, help } = parseBridgeArgs(args);
if (help) { printHelp(); return; }
const cwd = resolve(".");
// 1. Enable configs and check workspace trust
const { enableConfigs, checkHasTrustDialogAccepted } = await import("configs");
enableConfigs();
if (!checkHasTrustDialogAccepted(false)) {
console.error(`Error: Workspace not trusted. Please run \`claude\` in ${cwd} first.`);
process.exit(1);
}
// 2. Initialize error logging and analytics
const { initializeErrorLogSink } = await import("error-log");
const { initializeAnalyticsSink } = await import("analytics");
initializeErrorLogSink();
initializeAnalyticsSink();
// 3. Set working directory state
const { setOriginalCwd, setCwdState } = await import("cwd");
setOriginalCwd(cwd);
setCwdState(cwd);
// 4. Validate OAuth
const { getClaudeAIOAuthTokens, clearOAuthTokenCache, checkAndRefreshOAuthTokenIfNeeded } =
await import("oauth");
if (!getClaudeAIOAuthTokens()?.accessToken) {
console.error(BRIDGE_LOGIN_ERROR);
process.exit(1);
}
// 5. First-run consent dialog
const { getGlobalConfig, saveGlobalConfig } = await import("configs");
if (!getGlobalConfig().remoteDialogSeen) {
const rl = createInterface({ input: process.stdin, output: process.stdout });
console.log(`
Remote Control lets you access this CLI session from the web (claude.ai/code)
or the Claude app, so you can pick up where you left off on any device.
You can disconnect remote access anytime by running /remote-control again.
`);
const answer = await new Promise((resolve) => {
rl.question("Enable Remote Control? (y/n) ", resolve);
});
rl.close();
saveGlobalConfig((cfg) => {
if (cfg.remoteDialogSeen) return cfg;
return { ...cfg, remoteDialogSeen: true };
});
if (answer.toLowerCase() !== "y" && answer.toLowerCase() !== "yes") {
process.exit(0);
}
}
// 6. Security: reject non-HTTPS (except localhost)
const apiBaseUrl = getOauthConfig().BASE_API_URL;
if (apiBaseUrl.startsWith("http://") &&
!apiBaseUrl.includes("localhost") &&
!apiBaseUrl.includes("127.0.0.1")) {
console.error("Error: Remote Control base URL uses HTTP. Only HTTPS or localhost HTTP is allowed.");
process.exit(1);
}
// 7. Gather git info
const branch = await getBranch();
const gitRepoUrl = await getRemoteUrl();
const machineName = getMachineName();
const bridgeId = randomUUID();
const config = {
dir: cwd,
machineName,
branch,
gitRepoUrl,
maxSessions: 1,
verbose,
sandbox,
bridgeId,
environmentId: randomUUID(),
apiBaseUrl,
sessionIngressUrl: apiBaseUrl,
debugFile,
sessionTimeoutMs,
};
// 8. Create API client
const apiClient = createBridgeApiClient({
baseUrl: apiBaseUrl,
getAccessToken: () => getClaudeAIOAuthTokens()?.accessToken,
runnerVersion: "2.1.52",
onDebug: debug,
});
// 9. Register environment
let environmentId, environmentSecret;
try {
const result = await apiClient.registerBridgeEnvironment(config);
environmentId = result.environment_id;
environmentSecret = result.environment_secret;
} catch (err) {
trackEvent("tengu_bridge_registration_failed", {
status: err instanceof BridgeFatalError ? err.status : undefined,
});
console.error(
err instanceof BridgeFatalError && err.status === 404
? "Remote Control environments are not available for your account."
: `Error: ${err instanceof Error ? err.message : String(err)}`
);
process.exit(1);
}
// 10. Create session spawner and status display
const sessionSpawner = createSessionSpawner({
execPath: process.execPath,
env: process.env,
verbose,
sandbox,
debugFile,
onDebug: debug,
onActivity: (sessionId, activity) => { /* ... */ },
onPermissionRequest: (sessionId, request, token) => { /* ... */ },
});
const statusDisplay = createStatusDisplay({ verbose });
// 11. Create initial session
let initialSessionId = null;
try {
initialSessionId = await createBridgeSession({
environmentId,
title: "Remote Control session",
events: [],
gitRepoUrl,
branch,
signal: abortController.signal,
});
} catch (err) {
debug(`[bridge:init] Session creation failed (non-fatal): ${err.message}`);
}
// 12. Set up signal handlers
const abortController = new AbortController();
const onSigInt = () => { debug("[bridge:shutdown] SIGINT received"); abortController.abort(); };
const onSigTerm = () => { debug("[bridge:shutdown] SIGTERM received"); abortController.abort(); };
process.on("SIGINT", onSigInt);
process.on("SIGTERM", onSigTerm);
// 13. Set up stdin for raw key input (Ctrl+C, Space=toggle QR)
const onKeyPress = (data) => {
if (data[0] === 3 || data[0] === 4) { process.emit("SIGINT"); return; }
if (data[0] === 32) statusDisplay.toggleQr();
};
if (process.stdin.isTTY) {
process.stdin.setRawMode(true);
process.stdin.resume();
process.stdin.on("data", onKeyPress);
}
// 14. Enter main poll loop
try {
await mainPollLoop(
config,
environmentId,
environmentSecret,
apiClient,
sessionSpawner,
statusDisplay,
abortController.signal,
undefined, // backoff config (uses defaults)
Infinity, // max lifetime
initialSessionId,
async () => {
clearOAuthTokenCache();
await checkAndRefreshOAuthTokenIfNeeded();
return getClaudeAIOAuthTokens()?.accessToken;
}
);
} finally {
// Cleanup
process.off("SIGINT", onSigInt);
process.off("SIGTERM", onSigTerm);
process.stdin.off("data", onKeyPress);
if (process.stdin.isTTY) process.stdin.setRawMode(false);
process.stdin.pause();
}
process.exit(0);
}// cli.js:537065-537380 (reconstructed)
// Backoff configuration defaults
const BACKOFF_DEFAULTS = {
connInitialMs: 2000,
connCapMs: 120000, // 2 minutes max between connection retries
connGiveUpMs: 600000, // 10 minutes before giving up on connection
generalInitialMs: 500,
generalCapMs: 30000, // 30 seconds max between general retries
generalGiveUpMs: 600000, // 10 minutes before giving up on general errors
};
const RETRYABLE_ERRORS = new Set([
"ECONNREFUSED", "ECONNRESET", "ETIMEDOUT", "ENETUNREACH", "EHOSTUNREACH",
]);
async function mainPollLoop(
config, // { dir, maxSessions, sandbox, verbose, ... }
environmentId,
environmentSecret,
apiClient,
sessionSpawner,
statusDisplay,
signal,
backoff = BACKOFF_DEFAULTS,
maxLifetimeMs = BRIDGE_MAX_LIFETIME_MS,
initialSessionId,
refreshTokenFn
) {
const localAbort = new AbortController();
if (signal.aborted) localAbort.abort();
else signal.addEventListener("abort", () => localAbort.abort(), { once: true });
const localSignal = localAbort.signal;
const activeSessions = new Map(); // sessionId -> spawned process handle
const sessionStartTimes = new Map(); // sessionId -> timestamp
const sessionWorkIds = new Map(); // sessionId -> workId
const sessionTimeouts = new Map(); // sessionId -> timeout handle
const completedWorkIds = new Set();
// Token refresh scheduler (refreshes access tokens for active sessions)
const tokenRefresher = refreshTokenFn
? createTokenRefreshScheduler({
getAccessToken: refreshTokenFn,
onRefresh: (sessionId, newToken) => {
const session = activeSessions.get(sessionId);
if (session) session.updateAccessToken(newToken);
},
label: "bridge",
})
: null;
const loopStartTime = Date.now();
// Reconnection state
let connBackoff = 0, generalBackoff = 0;
let connErrorStart = null, generalErrorStart = null;
let lastPollTime = null;
// Status update interval
let statusInterval = null;
statusDisplay.printBanner(config, environmentId);
if (initialSessionId) statusDisplay.setAttached(initialSessionId);
function updateStatus() {
if (activeSessions.size === 0) {
statusDisplay.updateIdleStatus();
return;
}
const [sessionId, handle] = [...activeSessions.entries()].pop();
const startTime = sessionStartTimes.get(sessionId);
if (!startTime) return;
const currentActivity = handle.currentActivity;
if (!currentActivity || currentActivity.type === "result" || currentActivity.type === "error") return;
const elapsed = formatDuration(Date.now() - startTime);
const recentTools = handle.activities
.filter((a) => a.type === "tool_start")
.slice(-5)
.map((a) => a.summary);
statusDisplay.updateSessionStatus(sessionId, elapsed, currentActivity, recentTools);
}
function startStatusUpdates() {
stopStatusUpdates();
updateStatus();
statusInterval = setInterval(updateStatus, 1000);
}
function stopStatusUpdates() {
if (statusInterval) { clearInterval(statusInterval); statusInterval = null; }
}
// Session exit handler factory
function onSessionExit(sessionId, startTime, handle) {
return (exitStatus) => {
const workId = sessionWorkIds.get(sessionId);
activeSessions.delete(sessionId);
sessionStartTimes.delete(sessionId);
sessionWorkIds.delete(sessionId);
const timeout = sessionTimeouts.get(sessionId);
if (timeout) { clearTimeout(timeout); sessionTimeouts.delete(sessionId); }
tokenRefresher?.cancel(sessionId);
const duration = Date.now() - startTime;
debug(`[bridge:session] sessionId=${sessionId} exited status=${exitStatus} duration=${formatDuration(duration)}`);
trackEvent("tengu_bridge_session_done", { status: exitStatus, duration_ms: duration });
statusDisplay.clearStatus();
stopStatusUpdates();
const stderr = handle.lastStderr.length > 0 ? handle.lastStderr.join("\n") : undefined;
switch (exitStatus) {
case "completed":
statusDisplay.logSessionComplete(sessionId, duration);
statusDisplay.logStatus(`To reconnect: claude --resume ${sessionId}`);
break;
case "failed":
const errorMsg = stderr ?? "Process exited with error";
statusDisplay.logSessionFailed(sessionId, errorMsg);
statusDisplay.logStatus(`To reconnect: claude --resume ${sessionId}`);
break;
case "interrupted":
statusDisplay.logVerbose(`Session ${sessionId} interrupted`);
break;
}
// Archive session and track completed work
if (exitStatus !== "interrupted" && workId) {
pollSessionTitle(apiClient, sessionId, statusDisplay, activeSessions);
completedWorkIds.add(workId);
}
// If session ended non-interruptedly, abort loop to tear down environment
if (exitStatus !== "interrupted" && !localSignal.aborted) {
debug("[bridge:session] Session ended, aborting poll loop to tear down environment");
localAbort.abort();
return;
}
// All sessions done, go back to idle
if (activeSessions.size === 0 && !localSignal.aborted) {
debug("[bridge:work] All sessions finished, transitioning to idle");
startStatusUpdates();
}
};
}
// Start idle status if no initial session
if (!initialSessionId) startStatusUpdates();
// ═══════════════════════ MAIN LOOP ═══════════════════════
while (!localSignal.aborted) {
// Check max lifetime
if (maxLifetimeMs !== Infinity && Date.now() - loopStartTime >= maxLifetimeMs) {
statusDisplay.logStatus("Maximum runtime reached, shutting down…");
break;
}
const AT_CAPACITY_DELAY = 30000;
try {
// Long-poll for work
const work = await apiClient.pollForWork(environmentId, environmentSecret, localSignal);
// On successful poll, reset all error state
if (connErrorStart !== null || generalErrorStart !== null) {
const disconnectedMs = Date.now() - (connErrorStart ?? generalErrorStart ?? Date.now());
statusDisplay.logReconnected(disconnectedMs);
trackEvent("tengu_bridge_reconnected", { disconnected_ms: disconnectedMs });
}
connBackoff = 0; generalBackoff = 0;
connErrorStart = null; generalErrorStart = null;
lastPollTime = null;
// No work available
if (!work) {
await sleep(activeSessions.size >= config.maxSessions ? AT_CAPACITY_DELAY : 1000, localSignal);
continue;
}
// At capacity
if (activeSessions.size >= config.maxSessions) {
debug(`[bridge:work] At capacity (${activeSessions.size}/${config.maxSessions}), skipping`);
await sleep(AT_CAPACITY_DELAY, localSignal);
continue;
}
// Already completed this work
if (completedWorkIds.has(work.id)) {
debug(`[bridge:work] Skipping already-completed workId=${work.id}`);
await sleep(1000, localSignal);
continue;
}
// Decode work secret (contains session_ingress_token)
let workSecret;
try {
workSecret = decodeWorkSecret(work.secret);
} catch (err) {
statusDisplay.logError(`Failed to decode work secret for workId=${work.id}: ${err.message}`);
trackEvent("tengu_bridge_work_secret_failed", {});
continue;
}
// Handle work by type
switch (work.data.type) {
case "healthcheck":
debug("[bridge:work] Healthcheck received");
break;
case "session": {
const sessionId = work.data.id;
validateParam(sessionId, "session_id");
// Token update for existing session
const existing = activeSessions.get(sessionId);
if (existing) {
existing.updateAccessToken(workSecret.session_ingress_token);
tokenRefresher?.schedule(sessionId, workSecret.session_ingress_token);
debug(`[bridge:work] Updated access token for existing sessionId=${sessionId}`);
break;
}
// Reject if a different session is active
if (activeSessions.size > 0) {
debug(`[bridge:work] Rejecting foreign sessionId=${sessionId} while other sessions active`);
break;
}
// Build the SDK URL for the child process
const sdkUrl = buildSessionIngressUrl(config.sessionIngressUrl, sessionId);
debug(`[bridge:session] Spawning sessionId=${sessionId} sdkUrl=${sdkUrl}`);
trackEvent("tengu_bridge_session_started", { active_sessions: activeSessions.size });
// Spawn child claude process
const handle = sessionSpawner.spawn(
{ sessionId, sdkUrl, accessToken: workSecret.session_ingress_token },
config.dir
);
activeSessions.set(sessionId, handle);
sessionWorkIds.set(sessionId, work.id);
const startTime = Date.now();
sessionStartTimes.set(sessionId, startTime);
statusDisplay.logSessionStart(sessionId, `Session ${sessionId}`);
startStatusUpdates();
statusDisplay.setAttached(sessionId);
// Poll for session title asynchronously
pollSessionTitle(apiClient, sessionId, statusDisplay, activeSessions);
// Set session timeout
const timeoutMs = config.sessionTimeoutMs ?? DEFAULT_SESSION_TIMEOUT_MS;
if (timeoutMs > 0) {
const timer = setTimeout(() => {
debug(`[bridge:session] sessionId=${sessionId} timed out after ${formatDuration(timeoutMs)}`);
trackEvent("tengu_bridge_session_timeout", { timeout_ms: timeoutMs });
statusDisplay.logSessionFailed(sessionId, `Session timed out after ${formatDuration(timeoutMs)}`);
handle.kill();
}, timeoutMs);
sessionTimeouts.set(sessionId, timer);
}
// Schedule token refresh and set up exit handler
tokenRefresher?.schedule(sessionId, workSecret.session_ingress_token);
handle.done.then(onSessionExit(sessionId, startTime, handle));
break;
}
default:
debug(`[bridge:work] Unknown work type: ${work.data.type}, skipping`);
break;
}
} catch (err) {
if (localSignal.aborted) break;
if (err instanceof BridgeFatalError) {
if (isExpiredError(err.errorType)) statusDisplay.logStatus(err.message);
else statusDisplay.logError(err.message);
trackEvent("tengu_bridge_fatal_error", { status: err.status, error_type: err.errorType });
break;
}
const errMsg = extractErrorMessage(err);
const now = Date.now();
// Detect system sleep (large time gap)
if (lastPollTime !== null && now - lastPollTime > getSleepThreshold(backoff)) {
debug(`[bridge:work] Detected system sleep, resetting error budget`);
connErrorStart = null; connBackoff = 0;
generalErrorStart = null; generalBackoff = 0;
}
lastPollTime = now;
if (isConnectionError(err) || isTimeoutError(err)) {
// Connection error: exponential backoff with separate budget
if (!connErrorStart) connErrorStart = now;
const elapsed = now - connErrorStart;
if (elapsed >= backoff.connGiveUpMs) {
statusDisplay.logError(`Server unreachable for ${Math.round(elapsed / 60000)} minutes, giving up.`);
trackEvent("tengu_bridge_poll_give_up", { error_type: "connection", elapsed_ms: elapsed });
break;
}
generalErrorStart = null; generalBackoff = 0;
connBackoff = connBackoff ? Math.min(connBackoff * 2, backoff.connCapMs) : backoff.connInitialMs;
const delay = addJitter(connBackoff);
statusDisplay.updateReconnectingStatus(formatMs(delay), formatDuration(elapsed));
await sleep(delay, localSignal);
} else {
// General error: separate exponential backoff budget
if (!generalErrorStart) generalErrorStart = now;
const elapsed = now - generalErrorStart;
if (elapsed >= backoff.generalGiveUpMs) {
statusDisplay.logError(`Persistent errors for ${Math.round(elapsed / 60000)} minutes, giving up.`);
trackEvent("tengu_bridge_poll_give_up", { error_type: "general", elapsed_ms: elapsed });
break;
}
connErrorStart = null; connBackoff = 0;
generalBackoff = generalBackoff ? Math.min(generalBackoff * 2, backoff.generalCapMs) : backoff.generalInitialMs;
const delay = addJitter(generalBackoff);
statusDisplay.updateReconnectingStatus(formatMs(delay), formatDuration(elapsed));
await sleep(delay, localSignal);
}
}
}
// ═══════════════════════ SHUTDOWN ═══════════════════════
stopStatusUpdates();
statusDisplay.clearStatus();
const totalDuration = Date.now() - loopStartTime;
trackEvent("tengu_bridge_shutdown", { active_sessions: activeSessions.size, loop_duration_ms: totalDuration });
// Kill remaining active sessions
if (activeSessions.size > 0) {
debug(`[bridge:shutdown] Shutting down ${activeSessions.size} active session(s)`);
// ... graceful shutdown of child processes, then deregister environment
}
}
// Cancellable sleep helper
function sleep(ms, signal) {
if (signal?.aborted) return Promise.resolve();
return new Promise((resolve) => {
const timer = setTimeout(resolve, ms);
if (signal) {
signal.addEventListener("abort", () => { clearTimeout(timer); resolve(); }, { once: true });
}
});
}// cli.js:536242-536410 (reconstructed)
import { spawn } from "child_process";
import { createInterface } from "readline";
import { createWriteStream } from "fs";
import { tmpdir } from "os";
import { join, dirname } from "path";
function sanitizeId(id) {
return id.replace(/[^a-zA-Z0-9_-]/g, "_");
}
function createSessionSpawner(options) {
// options: { execPath, env, verbose, sandbox, debugFile, onDebug, onActivity, onPermissionRequest }
return {
spawn(sessionInfo, cwd) {
// sessionInfo: { sessionId, sdkUrl, accessToken }
const safeId = sanitizeId(sessionInfo.sessionId);
// Determine debug log file path
let debugLogFile;
if (options.debugFile) {
const dotIdx = options.debugFile.lastIndexOf(".");
debugLogFile = dotIdx > 0
? `${options.debugFile.slice(0, dotIdx)}-${safeId}${options.debugFile.slice(dotIdx)}`
: `${options.debugFile}-${safeId}`;
} else if (options.verbose) {
debugLogFile = join(tmpdir(), "claude", `bridge-session-${safeId}.log`);
}
// Build child process arguments
const childArgs = [
"--print",
"--sdk-url", sessionInfo.sdkUrl,
"--session-id", sessionInfo.sessionId,
"--input-format", "stream-json",
"--output-format", "stream-json",
"--replay-user-messages",
...(options.verbose ? ["--verbose"] : []),
...(debugLogFile ? ["--debug-file", debugLogFile] : []),
];
// Environment variables for the child
const childEnv = {
...options.env,
CLAUDE_CODE_OAUTH_TOKEN: undefined,
CLAUDE_CODE_ENVIRONMENT_KIND: "bridge",
...(options.sandbox && { CLAUDE_CODE_FORCE_SANDBOX: "1" }),
CLAUDE_CODE_SESSION_ACCESS_TOKEN: sessionInfo.accessToken,
CLAUDE_CODE_POST_FOR_SESSION_INGRESS_V2: "1",
};
options.onDebug(
`[bridge:session] Spawning sessionId=${sessionInfo.sessionId} sdkUrl=${sessionInfo.sdkUrl}`
);
// Spawn the child claude process
const child = spawn(options.execPath, childArgs, {
cwd,
stdio: ["pipe", "pipe", "pipe"],
env: childEnv,
windowsHide: true,
});
const activities = [];
let currentActivity = null;
const lastStderr = [];
// Read stderr for debug output
if (child.stderr) {
createInterface({ input: child.stderr }).on("line", (line) => {
if (options.verbose) process.stderr.write(line + "\n");
if (lastStderr.length >= 50) lastStderr.shift();
lastStderr.push(line);
});
}
// Read stdout for structured session output (stream-json)
if (child.stdout) {
createInterface({ input: child.stdout }).on("line", (line) => {
// Parse activity events from JSON lines
const events = parseActivityEvents(line, sessionInfo.sessionId, options.onDebug);
for (const event of events) {
if (activities.length >= 100) activities.shift();
activities.push(event);
currentActivity = event;
options.onActivity?.(sessionInfo.sessionId, event);
}
// Handle control requests (permission, interrupt)
let parsed;
try { parsed = JSON.parse(line); } catch {}
if (parsed && typeof parsed === "object" && parsed.type === "control_request") {
const request = parsed.request;
if (request?.subtype === "can_use_tool" && options.onPermissionRequest) {
options.onPermissionRequest(sessionInfo.sessionId, parsed, sessionInfo.accessToken);
} else if (request?.subtype === "interrupt") {
if (!child.killed) {
options.onDebug(`[bridge:session] Interrupt received, killing child pid=${child.pid}`);
child.kill(process.platform === "win32" ? undefined : "SIGTERM");
}
}
}
});
}
// Promise that resolves when the child exits
const done = new Promise((resolve) => {
child.on("close", (code, signal) => {
if (code === 0 || code === null) resolve("completed");
else resolve("failed");
});
});
return {
activities,
get currentActivity() { return currentActivity; },
lastStderr,
done,
kill() { child.kill(); },
updateAccessToken(token) {
// Write new token to child's stdin
child.stdin?.write(JSON.stringify({ type: "token_update", token }) + "\n");
},
};
},
};
}// cli.js:558482-559011 (reconstructed, key logic)
async function initReplBridge(options) {
const {
onInboundMessage,
onPermissionResponse,
onInterrupt,
onSetModel,
onSetMaxThinkingTokens,
onStateChange,
initialMessages,
previouslyFlushedUUIDs,
} = options ?? {};
// ---- Gate checks ----
if (!isBridgeEnabled()) return null;
await loadPolicies();
if (!isPolicyAllowed("allow_remote_sessions")) return null;
if (!getOAuthTokens()?.accessToken) return null;
if (!(await getOrgUUID())) return null;
// ---- Gather environment info ----
const branch = await getBranch();
const gitRepoUrl = await getRemoteUrl();
const apiBaseUrl = getOauthConfig().BASE_API_URL;
const cwd = getCwd();
const apiClient = createBridgeApiClient({
baseUrl: apiBaseUrl,
getAccessToken: () => getOAuthTokens()?.accessToken,
runnerVersion: "2.1.52",
onDebug: debug,
});
const config = {
dir: cwd,
machineName: hostname(),
branch,
gitRepoUrl,
maxSessions: 1,
verbose: false,
sandbox: false,
bridgeId: randomUUID(),
environmentId: randomUUID(),
apiBaseUrl,
sessionIngressUrl: apiBaseUrl,
};
// ---- Register environment ----
let environmentId, environmentSecret;
try {
const result = await apiClient.registerBridgeEnvironment(config);
environmentId = result.environment_id;
environmentSecret = result.environment_secret;
} catch (err) {
debug(`[bridge:repl] Environment registration failed: ${err.message}`);
return null;
}
// ---- Create session ----
// Derive title from resume flag or latest user message
let title = "Interactive session";
const resumeId = getResumeId();
if (resumeId) {
title = getSessionTitle(resumeId) ?? title;
} else if (initialMessages?.length > 0) {
const userMsgs = [...initialMessages].reverse().filter((m) => m.type === "user");
for (const msg of userMsgs) {
const text = extractTextContent(msg.message.content);
if (text) {
const stripped = stripMarkdown(text).trim();
if (stripped) {
title = stripped.length > 80 ? stripped.slice(0, 77) + "…" : stripped;
break;
}
}
}
}
const sessionId = await createBridgeSession({
environmentId,
title,
events: [],
gitRepoUrl,
branch,
signal: AbortSignal.timeout(15000),
});
if (!sessionId) {
await apiClient.deregisterEnvironment(environmentId).catch(() => {});
return null;
}
// ---- Set up deduplication ----
const flushedUUIDs = new Set();
if (initialMessages) for (const msg of initialMessages) flushedUUIDs.add(msg.uuid);
const deduper = new LRUSet(2000);
for (const uuid of flushedUUIDs) deduper.add(uuid);
// ---- Polling loop + WebSocket transport ----
const abortController = new AbortController();
let transport = null;
let currentWorkId = null;
let reconnectAttempts = 0;
const MAX_RECONNECTS = 3;
const bridgeContext = {
api: apiClient,
getCredentials: () => ({ environmentId, environmentSecret }),
signal: abortController.signal,
onStateChange,
// Handle work items from the poll loop
onWorkReceived: (workSessionId, ingressToken, workId) => {
if (workSessionId !== sessionId) {
debug(`[bridge:repl] Rejecting foreign session: expected=${sessionId} got=${workSessionId}`);
return;
}
currentWorkId = workId;
const oauthToken = getOAuthTokens()?.accessToken;
if (!oauthToken) return;
// Close existing transport if any
if (transport) { transport.close(); transport = null; }
// Handle control requests from remote
function handleControlRequest(request) {
if (!transport) return;
let response;
switch (request.request.subtype) {
case "initialize":
response = {
type: "control_response",
response: {
subtype: "success",
request_id: request.request_id,
response: {
commands: [],
output_style: "normal",
available_output_styles: ["normal"],
models: [],
account: {},
pid: process.pid,
},
},
};
break;
case "set_model":
onSetModel?.(request.request.model);
response = {
type: "control_response",
response: { subtype: "success", request_id: request.request_id },
};
break;
case "set_max_thinking_tokens":
onSetMaxThinkingTokens?.(request.request.max_thinking_tokens);
response = {
type: "control_response",
response: { subtype: "success", request_id: request.request_id },
};
break;
case "interrupt":
onInterrupt?.();
response = {
type: "control_response",
response: { subtype: "success", request_id: request.request_id },
};
break;
default:
response = {
type: "control_response",
response: {
subtype: "error",
request_id: request.request_id,
error: `REPL bridge does not handle control_request subtype: ${request.request.subtype}`,
},
};
}
transport.write({ ...response, session_id: sessionId });
}
// Create new HybridTransport (WebSocket + HTTP POST)
const ingressUrl = buildSessionIngressUrl(apiBaseUrl, workSessionId);
const newTransport = new HybridTransport(
new URL(ingressUrl),
{ Authorization: `Bearer ${oauthToken}`, "anthropic-version": "2023-06-01" },
workSessionId,
() => ({
Authorization: `Bearer ${getOAuthTokens()?.accessToken ?? oauthToken}`,
"anthropic-version": "2023-06-01",
})
);
transport = newTransport;
// On connect: flush initial messages
newTransport.setOnConnect(() => {
if (transport !== newTransport) return;
debug("[bridge:repl] Ingress transport connected");
onStateChange?.("connected");
// Flush initial messages on first connect
if (initialMessages?.length > 0) {
const toFlush = convertToTransportFormat(
initialMessages.filter(
(m) => (m.type === "user" || m.type === "assistant") && !previouslyFlushedUUIDs?.has(m.uuid)
)
);
if (toFlush.length > 0) {
debug(`[bridge:repl] Flushing ${toFlush.length} initial message(s)`);
const tagged = toFlush.map((e) => ({ ...e, session_id: sessionId }));
newTransport.writeBatch(tagged);
}
}
});
// On data: forward inbound messages, handle control requests
newTransport.setOnData((data) => {
processInboundData(data, deduper, onInboundMessage, onPermissionResponse, handleControlRequest);
});
// On close: handle reconnection
newTransport.setOnClose((code) => {
if (transport !== newTransport) return;
transport = null;
if (code === 1000) {
onStateChange?.("failed", "Remote Control session ended");
abortController.abort();
} else {
onStateChange?.("reconnecting", `Remote Control connection lost (code ${code})`);
// Fall back to poll loop for reconnection
if (currentWorkId) {
apiClient.stopWork(environmentId, currentWorkId, false).catch(() => {});
}
}
});
},
// Re-register environment when lost (e.g., server-side expiry)
async onEnvironmentLost() {
reconnectAttempts++;
if (reconnectAttempts > MAX_RECONNECTS) return null;
// Close transport, stop current work
if (transport) { transport.close(); transport = null; }
if (currentWorkId) {
await apiClient.stopWork(environmentId, currentWorkId, true).catch(() => {});
currentWorkId = null;
}
// Archive old session & deregister old environment
await archiveBridgeSession(sessionId).catch(() => {});
await apiClient.deregisterEnvironment(environmentId).catch(() => {});
// Re-register
try {
const result = await apiClient.registerBridgeEnvironment(config);
environmentId = result.environment_id;
environmentSecret = result.environment_secret;
} catch {
return null;
}
// Re-create session
const newSessionId = await createBridgeSession({
environmentId,
title: getSessionTitle(getResumeId()) ?? title,
events: [],
gitRepoUrl,
branch,
signal: AbortSignal.timeout(15000),
});
if (!newSessionId) return null;
sessionId = newSessionId;
reconnectAttempts = 0;
return { environmentId, environmentSecret };
},
};
// Return teardown function and bridge context
return {
teardown: () => {
abortController.abort();
if (transport) { transport.close(); transport = null; }
apiClient.deregisterEnvironment(environmentId).catch(() => {});
},
sessionId,
bridgeContext,
};
}// cli.js:553160-553239 (reconstructed)
const POST_MAX_RETRIES = 10;
const POST_INITIAL_BACKOFF_MS = 500;
const POST_MAX_BACKOFF_MS = 8000;
// Converts WebSocket URL to HTTP POST URL
function wsUrlToPostUrl(wsUrl) {
const protocol = wsUrl.protocol === "wss:" ? "https:" : "http:";
let pathname = wsUrl.pathname;
pathname = pathname.replace("/ws/", "/session/");
if (!pathname.endsWith("/events")) {
pathname = pathname.endsWith("/") ? pathname + "events" : pathname + "/events";
}
return `${protocol}//${wsUrl.host}${pathname}${wsUrl.search}`;
}
// HybridTransport extends a base WebSocket transport class
// Adds HTTP POST fallback for reliable event delivery
class HybridTransport extends BaseTransport {
postUrl;
constructor(url, headers, sessionId, getHeaders, options) {
super(url, headers, sessionId, getHeaders, options);
this.postUrl = wsUrlToPostUrl(url);
debug(`HybridTransport: POST URL = ${this.postUrl}`);
}
// Send a single event via HTTP POST
async write(event) {
return this.postEvents([event], event.type);
}
// Send multiple events in a batch via HTTP POST
async writeBatch(events) {
if (events.length === 0) return;
return this.postEvents(events, `batch(${events.length})`);
}
// Internal POST with retry logic
async postEvents(events, label) {
const sessionToken = getSessionToken();
if (!sessionToken) {
debug("HybridTransport: No session token available for POST");
return;
}
const headers = {
Authorization: `Bearer ${sessionToken}`,
"Content-Type": "application/json",
};
for (let attempt = 1; attempt <= POST_MAX_RETRIES; attempt++) {
try {
const response = await axios.post(
this.postUrl,
{ events },
{ headers, validateStatus: () => true }
);
if (response.status === 200 || response.status === 201) {
debug(`HybridTransport: POST success type=${label}`);
return;
}
// Client errors (except 429) are not retryable
if (response.status >= 400 && response.status < 500 && response.status !== 429) {
debug(`HybridTransport: POST returned ${response.status} (client error), not retrying`);
return;
}
debug(`HybridTransport: POST returned ${response.status}, attempt ${attempt}/${POST_MAX_RETRIES}`);
} catch (err) {
debug(`HybridTransport: POST error: ${err.message}, attempt ${attempt}/${POST_MAX_RETRIES}`);
}
if (attempt === POST_MAX_RETRIES) {
debug(`HybridTransport: POST failed after ${POST_MAX_RETRIES} attempts, continuing`);
return;
}
// Exponential backoff
const delay = Math.min(POST_INITIAL_BACKOFF_MS * Math.pow(2, attempt - 1), POST_MAX_BACKOFF_MS);
await new Promise((resolve) => setTimeout(resolve, delay));
}
}
}// cli.js:525393-525402 — Initial state shape
const initialBridgeState = {
replBridgeEnabled: false, // Is the bridge feature turned on?
replBridgeExplicit: false, // Did the user explicitly enable it (vs auto)?
replBridgeConnected: false, // Are we connected to the server?
replBridgeSessionActive: false, // Is a remote session actively running?
replBridgeReconnecting: false, // Are we attempting to reconnect?
replBridgeConnectUrl: undefined, // URL shown to user for connecting
replBridgeSessionUrl: undefined, // URL of active session
replBridgeEnvironmentId: undefined,
replBridgeSessionId: undefined,
replBridgeError: undefined, // Error message if failed
};
// cli.js:591727-591736 — Also initialized in REPL startup
// replBridgeEnabled defaults to `remoteControlAtStartup` config value
// State transitions:
// Enable: { replBridgeEnabled: true, replBridgeExplicit: true }
// Connected: { replBridgeConnected: true, replBridgeSessionActive: false, replBridgeConnectUrl, replBridgeSessionUrl, ... }
// Active: { replBridgeConnected: true, replBridgeSessionActive: true }
// Reconnect: { replBridgeReconnecting: true, replBridgeSessionActive: false }
// Error: { replBridgeError: message, replBridgeReconnecting: false, replBridgeSessionActive: false, replBridgeConnected: false }
// Disable: { replBridgeEnabled: false, replBridgeExplicit: false }
// Reset: all fields back to initial values// cli.js:536180-536240 (reconstructed)
// Maps internal tool names to human-readable labels
const TOOL_NAME_MAP = {
// e.g., "bash" -> "Bash", "read_file" -> "Read", etc.
};
function formatToolSummary(toolName, input) {
const label = TOOL_NAME_MAP[toolName] ?? toolName;
const detail =
input.file_path ??
input.filePath ??
input.pattern ??
input.command?.slice(0, 60) ??
input.url ??
input.query ??
"";
return detail ? `${label} ${detail}` : label;
}
function parseActivityEvents(jsonLine, sessionId, onDebug) {
let parsed;
try { parsed = JSON.parse(jsonLine); } catch { return []; }
if (!parsed || typeof parsed !== "object") return [];
const events = [];
const now = Date.now();
switch (parsed.type) {
case "assistant": {
const content = parsed.message?.content;
if (!Array.isArray(content)) break;
for (const block of content) {
if (!block || typeof block !== "object") continue;
if (block.type === "tool_use") {
const name = block.name ?? "Tool";
const input = block.input ?? {};
const summary = formatToolSummary(name, input);
events.push({ type: "tool_start", summary, timestamp: now });
onDebug(`[bridge:activity] sessionId=${sessionId} tool_use name=${name}`);
} else if (block.type === "text") {
const text = block.text ?? "";
if (text.length > 0) {
events.push({ type: "text", summary: text.slice(0, 80), timestamp: now });
onDebug(`[bridge:activity] sessionId=${sessionId} text "${text.slice(0, 100)}"`);
}
}
}
break;
}
case "result": {
const subtype = parsed.subtype;
if (subtype === "success") {
events.push({ type: "result", summary: "Session completed", timestamp: now });
} else if (subtype) {
const error = parsed.errors?.[0] ?? `Error: ${subtype}`;
events.push({ type: "error", summary: error, timestamp: now });
}
break;
}
}
return events;
}// cli.js:574799-574821 (reconstructed)
function BridgeStatusLine({ bridgeSelected }) {
const enabled = useStore((s) => s.replBridgeEnabled);
const connected = useStore((s) => s.replBridgeConnected);
const sessionActive = useStore((s) => s.replBridgeSessionActive);
const reconnecting = useStore((s) => s.replBridgeReconnecting);
const error = useStore((s) => s.replBridgeError);
const explicit = useStore((s) => s.replBridgeExplicit);
if (!isBridgeEnabled() || !enabled) return null;
const { label, color } = getStatusLabelAndColor({ error, connected, sessionActive, reconnecting });
// Only show non-error status if explicitly enabled
if (!explicit && label !== "Remote Control failed" && label !== "Remote Control reconnecting") {
return null;
}
return (
<Text color={bridgeSelected ? "background" : color} inverse={bridgeSelected} wrap="truncate">
{label}
{bridgeSelected && <Text dimColor> · Enter to view</Text>}
</Text>
);
}// cli.js:516435-516444
function createBridgeStatusMessage(url) {
return {
type: "system",
subtype: "bridge_status",
content: `/remote-control is active. Code in CLI or at ${url}`,
url,
isMeta: false,
timestamp: new Date().toISOString(),
uuid: generateUUID(),
};
}| HTTP Status | Handling |
|---|---|
| 200 | Success |
| 401 | Auto-refresh token, retry once; if still 401, throw fatal |
| 403 | Check if expired session; throw fatal |
| 404 | Environment not available; throw fatal |
| 410 | Session expired; throw fatal with "environment_expired" |
| 429 | Rate limited; throw (non-fatal, retry via backoff) |
| 5xx | Never reach (validateStatus filters < 500) |
Connection errors (ECONNREFUSED, ECONNRESET, ETIMEDOUT, etc.):
- Initial delay: 2 seconds
- Max delay: 2 minutes (exponential backoff with jitter)
- Give up after: 10 minutes continuous failure
General errors (unexpected API errors):
- Initial delay: 500ms
- Max delay: 30 seconds (exponential backoff with jitter)
- Give up after: 10 minutes continuous failure
System sleep detection:
- If time gap between polls exceeds threshold, reset error budgets
- Prevents giving up due to laptop sleep / suspend
1. HTTPS enforced:
- Non-localhost HTTP base URLs are rejected at startup
- Only HTTPS or localhost HTTP allowed
2. OAuth-only:
- Requires claude.ai subscription access tokens
- Tokens auto-refreshed on 401
3. Environment secrets:
- registerBridgeEnvironment returns environment_secret
- Used for polling authentication (separate from OAuth token)
4. Work secrets:
- Each work item contains an encrypted secret
- Decoded to extract session_ingress_token
- Used for WebSocket/POST authentication to session ingress
5. Session tokens:
- Periodically refreshed via token refresh scheduler
- Updated in child processes via stdin JSON message
6. Sandbox mode:
- Optional --sandbox flag sets CLAUDE_CODE_FORCE_SANDBOX=1
- Restricts file system access in child sessions
7. Workspace trust:
- Requires prior trust dialog acceptance for the working directory
- Prevents remote access to untrusted directories
8. Organization policy:
- "allow_remote_sessions" policy can disable feature org-wide
// Timing
const BRIDGE_MAX_LIFETIME_MS = 86400000; // 24 hours
const DEFAULT_SESSION_TIMEOUT_MS = 86400000; // 24 hours
const STATUS_UPDATE_INTERVAL_MS = 1000; // 1 second
// API
const ANTHROPIC_BETA_HEADER = "environments-2025-11-01";
const ANTHROPIC_VERSION = "2023-06-01";
// Backoff
const CONN_INITIAL_MS = 2000;
const CONN_CAP_MS = 120000; // 2 minutes
const CONN_GIVE_UP_MS = 600000; // 10 minutes
const GENERAL_INITIAL_MS = 500;
const GENERAL_CAP_MS = 30000; // 30 seconds
const GENERAL_GIVE_UP_MS = 600000; // 10 minutes
// Transport
const POST_MAX_RETRIES = 10;
const POST_INITIAL_BACKOFF_MS = 500;
const POST_MAX_BACKOFF_MS = 8000;
// Retryable network errors
const RETRYABLE_ERRORS = new Set([
"ECONNREFUSED", "ECONNRESET", "ETIMEDOUT", "ENETUNREACH", "EHOSTUNREACH",
]);
// Poll
const POLL_BLOCK_MS = 900;
const POLL_TIMEOUT_MS = 10000;
// Session spawner
const MAX_STDERR_LINES = 50;
const MAX_ACTIVITIES = 100;
// REPL bridge
const MAX_RECONNECT_ATTEMPTS = 3;
const UUID_DEDUP_CAPACITY = 2000;
// CLI aliases for the subcommand
const REMOTE_CONTROL_ALIASES = ["remote-control", "rc", "remote", "sync", "bridge"];
// Slash command aliases
const SLASH_COMMAND_ALIASES = ["rc"];
// Environment variables set on child processes
// CLAUDE_CODE_ENVIRONMENT_KIND = "bridge"
// CLAUDE_CODE_SESSION_ACCESS_TOKEN = <token>
// CLAUDE_CODE_POST_FOR_SESSION_INGRESS_V2 = "1"
// CLAUDE_CODE_FORCE_SANDBOX = "1" (if --sandbox)To implement a similar remote control system in another agent:
- API Layer: Implement the Environments API client (register, poll, ack, stop, deregister, sessions)
- Auth: OAuth token management with auto-refresh on 401
- Feature Gating: Feature flags + organization policy checks
- Standalone Mode: CLI subcommand that registers an environment and enters the poll loop
- Session Spawner: Spawn child agent processes with stream-json I/O for each remote session
- Poll Loop: Long-polling with dual-track exponential backoff (connection vs general errors), sleep detection
- In-Process Mode: Slash command that shares the current session with the remote UI
- Transport: WebSocket for real-time streaming + HTTP POST fallback for reliability
- Control Protocol: Handle
initialize,set_model,set_max_thinking_tokens,interruptcontrol requests - State Management: Track connection state, session state, errors, reconnection status
- Activity Tracking: Parse agent output to extract tool usage and text for status display
- Status Display: Show connection status, session activity, reconnection progress
- Graceful Shutdown: Handle SIGINT/SIGTERM, kill child processes, deregister environment
- Security: HTTPS enforcement, workspace trust, sandbox mode, token isolation