@@ -56,6 +56,10 @@ const (
5656 // Temporarily disable efficiency mode until we revisit this behavior.
5757 efficiencyModeEnabled = false
5858
59+ // windowsServiceModeEnabled controla o modo service-first.
60+ // Enquanto false, o runtime opera sempre em modo local (tray icon no logon).
61+ windowsServiceModeEnabled = false
62+
5963 WindowWidth = 1280
6064 WindowHeight = 860
6165 WindowMinWidth = 980
@@ -136,9 +140,9 @@ type App struct {
136140 zeroTouchAttemptInFlight atomic.Bool
137141 zeroTouchApprovalPending atomic.Bool
138142
139- // serviceConnectedMode é true quando o Windows Service foi detectado no startup.
140- // Quando ativo, workers locais de automação e inventário são omitidos para
141- // evitar duplicação com os workers do service (arquitetura service-first) .
143+ // serviceConnectedMode é true quando o Windows Service foi detectado no startup
144+ // E o modo service-first está habilitado. Enquanto o modo estiver desativado,
145+ // essa flag permanece false e o runtime fica sempre local .
142146 serviceConnectedMode atomic.Bool
143147
144148 // startupTime registra quando a aplicação iniciou, usado para calcular
@@ -162,8 +166,11 @@ func NewApp(opts AppStartupOptions) *App {
162166 reg := mcp .NewRegistry ()
163167 chatSvc := ai .NewService (reg )
164168
165- // Initialize service client for communicating with Windows Service
166- serviceClient := service .NewServiceClient ()
169+ var serviceClient * service.ServiceClient
170+ if windowsServiceModeEnabled {
171+ // Initialize service client for communicating with Windows Service.
172+ serviceClient = service .NewServiceClient ()
173+ }
167174
168175 a := & App {
169176 ctx : context .Background (), // inicializado para evitar nil; sobrescrito por SetContext()
@@ -526,12 +533,22 @@ func (a *App) shouldRunLocalP2P() bool {
526533 if a == nil {
527534 return false
528535 }
529- if a .serviceConnectedMode . Load () && ! a .runtimeFlags .DebugMode {
536+ if a .shouldUseServiceRuntime () && ! a .runtimeFlags .DebugMode {
530537 return false
531538 }
532539 return true
533540}
534541
542+ func (a * App ) shouldUseServiceRuntime () bool {
543+ if a == nil || ! windowsServiceModeEnabled {
544+ return false
545+ }
546+ if a .serviceClient == nil {
547+ return false
548+ }
549+ return a .serviceConnectedMode .Load ()
550+ }
551+
535552func (a * App ) startup (ctx context.Context ) {
536553 ctx , cancel := context .WithCancel (ctx )
537554 a .ctx = ctx
@@ -566,10 +583,11 @@ func (a *App) startup(ctx context.Context) {
566583 a .consolEngine = newConsolidationEngine (db , agentIDForEngine )
567584 }
568585
569- // Verificar conectividade com o Windows Service antes de iniciar workers locais.
570- // Quando o service está disponível, workers de automação e inventário são omitidos
571- // para evitar duplicação (arquitetura service-first: service executa, UI consome).
572- if a .serviceClient != nil {
586+ // Modo padrão: runtime local (tray icon no logon), sem service-first.
587+ if ! windowsServiceModeEnabled {
588+ a .serviceConnectedMode .Store (false )
589+ log .Println ("[startup] modo Windows Service desativado — runtime local (tray) ativo" )
590+ } else if a .serviceClient != nil {
573591 probeCtx , probeCancel := context .WithTimeout (ctx , 2 * time .Second )
574592 if a .serviceClient .Ping (probeCtx ) {
575593 a .serviceConnectedMode .Store (true )
@@ -585,7 +603,7 @@ func (a *App) startup(ctx context.Context) {
585603 defer a .startupWg .Done ()
586604
587605 // Quando o service está disponível, ele já gerencia inventário; pular coleta local.
588- if a .serviceConnectedMode . Load () {
606+ if a .shouldUseServiceRuntime () {
589607 log .Println ("[startup] inventory-startup: ignorado (service disponível)" )
590608 return
591609 }
@@ -622,7 +640,7 @@ func (a *App) startup(ctx context.Context) {
622640 if a .debugSvc != nil {
623641 a .debugSvc .BootstrapAgentCredentialsFromInstallerConfig (ctx )
624642 }
625- if a .serviceConnectedMode . Load () {
643+ if a .shouldUseServiceRuntime () {
626644 a .requestServiceConfigReload (ctx , "startup-bootstrap" )
627645 }
628646
@@ -631,7 +649,7 @@ func (a *App) startup(ctx context.Context) {
631649 a .ensureMeshCentralInstalled (ctx , "startup-auth" , false )
632650 })
633651
634- if a .serviceConnectedMode . Load () {
652+ if a .shouldUseServiceRuntime () {
635653 log .Println ("[startup] agent-runtime local: ignorado (service disponível)" )
636654 return
637655 }
@@ -663,7 +681,7 @@ func (a *App) startup(ctx context.Context) {
663681 }
664682
665683 // Quando o service está disponível, ele gerencia automação; pular worker local.
666- if a .serviceConnectedMode . Load () {
684+ if a .shouldUseServiceRuntime () {
667685 log .Println ("[startup] automation-service: ignorado (service disponível)" )
668686 return
669687 }
@@ -690,7 +708,7 @@ func (a *App) startup(ctx context.Context) {
690708 log .Println ("[startup] p2p local: ignorado (service disponível)" )
691709 return
692710 }
693- if a .serviceConnectedMode . Load () && a .runtimeFlags .DebugMode {
711+ if a .shouldUseServiceRuntime () && a .runtimeFlags .DebugMode {
694712 log .Println ("[startup] p2p local: iniciado em modo debug mesmo com service disponível" )
695713 }
696714 if ! isAgentConfigured () && a .zeroTouchConfigRegistrationAllowed () {
@@ -742,7 +760,7 @@ func (a *App) SendTestHeartbeat() string {
742760 defer a .queuedForceHeartbeat .Store (false )
743761
744762 a .logs .append ("[heartbeat][manual] enviando heartbeat manual..." )
745- if a .serviceConnectedMode . Load () && a . serviceClient != nil {
763+ if a .shouldUseServiceRuntime () {
746764 message , err := a .requestServiceForceHeartbeat (a .ctx , "debug-manual-heartbeat" )
747765 if err != nil {
748766 a .logs .append ("[heartbeat][manual] falha ao enviar heartbeat manual via service: " + err .Error ())
@@ -997,11 +1015,24 @@ func serviceOnlyUnavailablePayload(detail string) dto.ServiceHealthPayload {
9971015 }
9981016}
9991017
1018+ func localRuntimeHealthPayload () dto.ServiceHealthPayload {
1019+ return dto.ServiceHealthPayload {
1020+ Running : true ,
1021+ ServiceOnly : false ,
1022+ UserMessage : "Runtime local ativo (tray icon no logon)." ,
1023+ }
1024+ }
1025+
10001026// GetServiceHealth returns the health of the headless Windows Service.
10011027// Retained as map[string]interface{} for Wails frontend compatibility.
10021028func (a * App ) GetServiceHealth () map [string ]interface {} {
10031029 var payload dto.ServiceHealthPayload
10041030
1031+ if ! windowsServiceModeEnabled {
1032+ payload = localRuntimeHealthPayload ()
1033+ return toMap (payload )
1034+ }
1035+
10051036 if a .serviceClient == nil {
10061037 payload = serviceOnlyUnavailablePayload ("service client not initialized" )
10071038 return toMap (payload )
0 commit comments