@@ -20,6 +20,7 @@ import { configureConsoleForHeadless, safeStderr } from "./init.js";
2020import { sentryService } from "./sentry.js" ;
2121import { addCommonOptions , mergeParentOptions } from "./shared-options.js" ;
2222import { posthogService } from "./telemetry/posthogService.js" ;
23+ import { post } from "./util/apiClient.js" ;
2324import { gracefulExit } from "./util/exit.js" ;
2425import { logger } from "./util/logger.js" ;
2526import { readStdinSync } from "./util/stdin.js" ;
@@ -31,6 +32,9 @@ let showExitMessage: boolean;
3132let exitMessageCallback : ( ( ) => void ) | null ;
3233let lastCtrlCTime : number ;
3334
35+ // Agent ID for serve mode - set when serve command is invoked with --id
36+ let agentId : string | undefined ;
37+
3438// Initialize state immediately to avoid temporal dead zone issues with exported functions
3539( function initializeTUIState ( ) {
3640 tuiUnmount = null ;
@@ -39,6 +43,11 @@ let lastCtrlCTime: number;
3943 lastCtrlCTime = 0 ;
4044} ) ( ) ;
4145
46+ // Set the agent ID for error reporting (called by serve command)
47+ export function setAgentId ( id : string | undefined ) {
48+ agentId = id ;
49+ }
50+
4251// Register TUI cleanup function for graceful shutdown
4352export function setTUIUnmount ( unmount : ( ) = > void ) {
4453 tuiUnmount = unmount ;
@@ -89,6 +98,27 @@ export function shouldShowExitMessage(): boolean {
8998 return showExitMessage ;
9099}
91100
101+ // Helper to report unhandled errors to the API when running in serve mode
102+ async function reportUnhandledErrorToApi ( error : Error ) : Promise < void > {
103+ if ( ! agentId ) {
104+ // Not running in serve mode with an agent ID, skip API reporting
105+ return ;
106+ }
107+
108+ try {
109+ await post ( `agents/${ agentId } /status` , {
110+ status : "FAILED" ,
111+ errorMessage : `Unhandled error: ${ error . message } ` ,
112+ } ) ;
113+ logger . debug ( `Reported unhandled error to API for agent ${ agentId } ` ) ;
114+ } catch ( apiError ) {
115+ // If API reporting fails, just log it - don't crash
116+ logger. debug (
117+ `Failed to report error to API: ${ apiError instanceof Error ? apiError . message : String ( apiError ) } ` ,
118+ ) ;
119+ }
120+ }
121+
92122// Add global error handlers to prevent uncaught errors from crashing the process
93123process . on ( "unhandledRejection" , ( reason , promise ) => {
94124 // Extract useful information from the reason
@@ -101,13 +131,21 @@ process.on("unhandledRejection", (reason, promise) => {
101131 // If reason is an Error, use it directly for better stack traces
102132 if ( reason instanceof Error ) {
103133 logger . error ( "Unhandled Promise Rejection" , reason , errorDetails ) ;
134+ // Report to API if running in serve mode
135+ reportUnhandledErrorToApi ( reason ) . catch ( ( ) => {
136+ // Silently fail if API reporting errors - already logged in helper
137+ } ) ;
104138 } else {
105139 // Convert non-Error reasons to Error for consistent handling
106140 const error = new Error ( `Unhandled rejection: ${ String ( reason ) } ` ) ;
107141 logger . error ( "Unhandled Promise Rejection" , error , {
108142 ...errorDetails ,
109143 originalReason : String ( reason ) ,
110144 } ) ;
145+ // Report to API if running in serve mode
146+ reportUnhandledErrorToApi ( error ) . catch ( ( ) => {
147+ // Silently fail if API reporting errors - already logged in helper
148+ } ) ;
111149 }
112150
113151 // Note: Sentry capture is handled by logger.error() above
@@ -116,6 +154,10 @@ process.on("unhandledRejection", (reason, promise) => {
116154
117155process . on ( "uncaughtException" , ( error ) => {
118156 logger . error ( "Uncaught Exception:" , error ) ;
157+ // Report to API if running in serve mode
158+ reportUnhandledErrorToApi ( error ) . catch ( ( ) => {
159+ // Silently fail if API reporting errors - already logged in helper
160+ } ) ;
119161 // Note: Sentry capture is handled by logger.error() above
120162 // Don't exit the process, just log the error
121163} ) ;
0 commit comments