(
config: BridgeConfig,
environmentId: string,
environmentSecret: string,
api: BridgeApiClient,
spawner: SessionSpawner,
logger: BridgeLogger,
signal: AbortSignal,
backoffConfig: BackoffConfig = DEFAULT_BACKOFF,
initialSessionId?: string,
runtimeAuth?: Pick<
ManagedRemoteRuntimeAuth,
'getAccessToken' | 'refreshAccessToken'
>,
)
| 145 | } |
| 146 | |
| 147 | export async function runBridgeLoop( |
| 148 | config: BridgeConfig, |
| 149 | environmentId: string, |
| 150 | environmentSecret: string, |
| 151 | api: BridgeApiClient, |
| 152 | spawner: SessionSpawner, |
| 153 | logger: BridgeLogger, |
| 154 | signal: AbortSignal, |
| 155 | backoffConfig: BackoffConfig = DEFAULT_BACKOFF, |
| 156 | initialSessionId?: string, |
| 157 | runtimeAuth?: Pick< |
| 158 | ManagedRemoteRuntimeAuth, |
| 159 | 'getAccessToken' | 'refreshAccessToken' |
| 160 | >, |
| 161 | ): Promise<void> { |
| 162 | // Local abort controller so that onSessionDone can stop the poll loop. |
| 163 | // Linked to the incoming signal so external aborts also work. |
| 164 | const controller = new AbortController() |
| 165 | if (signal.aborted) { |
| 166 | controller.abort() |
| 167 | } else { |
| 168 | signal.addEventListener('abort', () => controller.abort(), { once: true }) |
| 169 | } |
| 170 | const loopSignal = controller.signal |
| 171 | |
| 172 | const activeSessions = new Map<string, SessionHandle>() |
| 173 | const sessionStartTimes = new Map<string, number>() |
| 174 | const sessionWorkIds = new Map<string, string>() |
| 175 | // Compat-surface ID (session_*) computed once at spawn and cached so |
| 176 | // cleanup and status-update ticks use the same key regardless of whether |
| 177 | // the ncode_bridge_repl_v2_cse_shim_enabled gate flips mid-session. |
| 178 | const sessionCompatIds = new Map<string, string>() |
| 179 | // Session ingress JWTs for heartbeat auth, keyed by sessionId. |
| 180 | // Stored separately from handle.accessToken because the token refresh |
| 181 | // scheduler overwrites that field with the OAuth token (~3h55m in). |
| 182 | const sessionIngressTokens = new Map<string, string>() |
| 183 | const sessionTimers = new Map<string, ReturnType<typeof setTimeout>>() |
| 184 | const completedWorkIds = new Set<string>() |
| 185 | const sessionWorktrees = new Map< |
| 186 | string, |
| 187 | { |
| 188 | worktreePath: string |
| 189 | worktreeBranch?: string |
| 190 | gitRoot?: string |
| 191 | hookBased?: boolean |
| 192 | } |
| 193 | >() |
| 194 | // Track sessions killed by the timeout watchdog so onSessionDone can |
| 195 | // distinguish them from server-initiated or shutdown interrupts. |
| 196 | const timedOutSessions = new Set<string>() |
| 197 | // Sessions that already have a title (server-set or bridge-derived) so |
| 198 | // onFirstUserMessage doesn't clobber a user-assigned --name / web rename. |
| 199 | // Keyed by compatSessionId to match logger.setSessionTitle's key. |
| 200 | const titledSessions = new Set<string>() |
| 201 | // Signal to wake the at-capacity sleep early when a session completes, |
| 202 | // so the bridge can immediately accept new work. |
| 203 | const capacityWake = createCapacityWake(loopSignal) |
| 204 |
no test coverage detected