1- import { acpLiveTopics , sessionSummarySchema , type SessionSummary } from '@emdash/core/acp' ;
2- import { LiveModelClient , type LiveSnapshot , type LiveUpdate } from '@emdash/core/live' ;
1+ import { sessionSummarySchema , type SessionSummary } from '@emdash/core/acp' ;
32import type { Unsubscribe } from '@emdash/shared' ;
3+ import { ReplicaState } from '@emdash/wire' ;
44import { z } from 'zod' ;
55import { agentHookService } from '@main/core/agent-hooks/agent-hook-service' ;
66import { isAppFocused } from '@main/core/agent-hooks/notification' ;
77import { log } from '@main/lib/logger' ;
88import { deriveAcpAgentStatusActions , type AcpAgentStatusAction } from './agent-status-transition' ;
9- import { acpWire } from './controller' ;
10- import { acpRuntimeProcessHost } from './runtime-process/host' ;
9+ import { getAcpRuntimeHandle } from './controller' ;
1110
1211type SessionSummaryList = Record < string , SessionSummary > ;
1312
1413const sessionSummaryListSchema = z . record ( z . string ( ) , sessionSummarySchema ) ;
1514
1615class AcpAgentStatusBridge {
1716 private readonly summaries = new Map < string , SessionSummary > ( ) ;
18- private startedUnsubscribe : Unsubscribe | null = null ;
19- private wireUnsubscribe : Unsubscribe | null = null ;
17+ private processExitUnsubscribe : Unsubscribe | null = null ;
18+ private replica : ReplicaState < SessionSummaryList > | null = null ;
2019 private attaching = false ;
2120
2221 initialize ( ) : void {
23- this . startedUnsubscribe = acpRuntimeProcessHost . onStarted ( ( ) => {
24- void this . attach ( ) . catch ( ( error ) => {
25- log . warn ( 'ACP agent status bridge failed to attach' , { error : String ( error ) } ) ;
26- } ) ;
22+ void this . attach ( ) . catch ( ( error ) => {
23+ log . warn ( 'ACP agent status bridge failed to attach' , { error : String ( error ) } ) ;
2724 } ) ;
2825 }
2926
3027 dispose ( ) : void {
31- this . startedUnsubscribe ?.( ) ;
32- this . startedUnsubscribe = null ;
28+ this . processExitUnsubscribe ?.( ) ;
29+ this . processExitUnsubscribe = null ;
3330 this . detach ( ) ;
3431 }
3532
@@ -38,58 +35,37 @@ class AcpAgentStatusBridge {
3835 this . attaching = true ;
3936 try {
4037 this . detach ( ) ;
41-
42- const topic = acpLiveTopics . sessionStateList . topic ( undefined ) ;
43- const client = new LiveModelClient < SessionSummaryList > (
44- sessionSummaryListSchema ,
45- ( ) => acpWire . live . snapshot ( topic ) as Promise < LiveSnapshot < SessionSummaryList > > ,
46- ( summaries ) => void this . applySummaries ( summaries )
47- ) ;
48- const buffer : LiveUpdate [ ] = [ ] ;
49- let seeded = false ;
50- let detachLive : Unsubscribe | null = null ;
51- let detachWire : Unsubscribe | null = null ;
52-
53- try {
54- detachLive = await acpWire . live . attach ( topic , ( update ) => {
55- if ( seeded ) {
56- client . applyUpdate ( update ) ;
57- } else {
58- buffer . push ( update ) ;
59- }
60- } ) ;
61- detachWire = acpWire . onDisconnect ( ( ) => {
62- void this . resetAll ( ) . catch ( ( error ) => {
63- log . warn ( 'ACP agent status bridge failed to reset statuses on disconnect' , {
64- error : String ( error ) ,
65- } ) ;
38+ const handle = await getAcpRuntimeHandle ( ) ;
39+ this . processExitUnsubscribe = handle . process . onExit ( ( exit ) => {
40+ if ( exit . willRestart ) return ;
41+ void this . resetAll ( ) . catch ( ( error ) => {
42+ log . warn ( 'ACP agent status bridge failed to reset statuses on disconnect' , {
43+ error : String ( error ) ,
6644 } ) ;
67- this . detach ( ) ;
6845 } ) ;
69-
70- client . seed ( ( await acpWire . live . snapshot ( topic ) ) as LiveSnapshot < SessionSummaryList > ) ;
71- seeded = true ;
72- for ( const update of buffer ) {
73- client . applyUpdate ( update ) ;
46+ this . detach ( ) ;
47+ } ) ;
48+ const replica = new ReplicaState < SessionSummaryList > (
49+ handle . client . sessions . state ( undefined , 'list' ) ,
50+ {
51+ schema : sessionSummaryListSchema ,
52+ onChange : ( summaries ) => void this . applySummaries ( summaries ) ,
7453 }
75-
76- this . wireUnsubscribe = ( ) => {
77- detachLive ?.( ) ;
78- detachWire ?.( ) ;
79- } ;
80- } catch ( error ) {
81- detachLive ?.( ) ;
82- detachWire ?.( ) ;
83- throw error ;
84- }
54+ ) ;
55+ await replica . ready ;
56+ this . replica = replica ;
57+ this . applySummaries ( replica . current ( ) ) ;
8558 } finally {
8659 this . attaching = false ;
8760 }
8861 }
8962
9063 private detach ( ) : void {
91- this . wireUnsubscribe ?.( ) ;
92- this . wireUnsubscribe = null ;
64+ this . processExitUnsubscribe ?.( ) ;
65+ this . processExitUnsubscribe = null ;
66+ const replica = this . replica ;
67+ this . replica = null ;
68+ if ( replica ) void replica . dispose ( ) ;
9369 }
9470
9571 private applySummaries ( nextSummaries : SessionSummaryList ) : void {
0 commit comments