@@ -53,6 +53,7 @@ import type {
5353} from './pi-tui-contracts.js' ;
5454import { AUTO_RECAP_DISPLAY_LIMIT_BYTES , shouldAutoRecap } from './session-recap.js' ;
5555import type { InvocableSkillEntry } from '@maka/runtime/skill-invocation' ;
56+ import type { AgentGraphClientSnapshot , AgentGraphEpochSummary } from '@maka/runtime-host/protocol' ;
5657import { MakaSkillHighlightEditor } from './skill-highlight-editor.js' ;
5758import { parseGraphCommand , type ParsedGraphCommand } from '@maka/core/graph-command' ;
5859import { parseSwarmCommand , type ParsedSwarmCommand } from '@maka/core/swarm-command' ;
@@ -166,6 +167,11 @@ export interface MakaPiTuiInput {
166167 listShellRunUpdates ?: ( sessionId : string ) => Promise < ShellRunUpdate [ ] > ;
167168 /** Host-owned invocable Skill catalog used for picker, completion, and token highlighting. */
168169 listSkills ?: ( cwd : string ) => Promise < readonly InvocableSkillEntry [ ] > ;
170+ /** Read-only Runtime Host projection for inspecting current and historical Graph epochs. */
171+ agentGraphHistory ?: {
172+ listEpochs ( rootSessionId : string ) : Promise < readonly AgentGraphEpochSummary [ ] > ;
173+ getSnapshot ( rootSessionId : string , graphId : string ) : Promise < AgentGraphClientSnapshot > ;
174+ } ;
169175 /** Serializes TUI turn and control activity for the attached Session. */
170176 turnActivity : MakaPiTuiTurnActivitySurface ;
171177 /** API-key onboarding surface (#1098). When present, /setup runs the wizard,
@@ -2264,6 +2270,56 @@ export async function runMakaPiTui(input: MakaPiTuiInput): Promise<void> {
22642270 requestRender ( ) ;
22652271 } ;
22662272
2273+ const showGraphHistory = async ( ) : Promise < void > => {
2274+ const rootSessionId = input . driver . getSessionId ( ) ;
2275+ if ( ! rootSessionId || ! input . agentGraphHistory ) {
2276+ throw new Error ( 'Agent Graph history is unavailable on this session driver.' ) ;
2277+ }
2278+ const epochs = await input . agentGraphHistory . listEpochs ( rootSessionId ) ;
2279+ const items : SelectItem [ ] = epochs . map ( ( entry ) => ( {
2280+ value : entry . graphId ,
2281+ label : `Run #${ entry . epoch } ${ entry . current ? ' · Current' : '' } ` ,
2282+ description : entry . current ? 'Current graph' : 'History · read-only' ,
2283+ } ) ) ;
2284+ if ( items . length === 0 ) {
2285+ state . entries . push ( {
2286+ kind : 'notice' ,
2287+ level : 'info' ,
2288+ text : 'This session has no Agent Graph runs.' ,
2289+ } ) ;
2290+ requestRender ( ) ;
2291+ return ;
2292+ }
2293+ const epochsByGraphId = new Map ( epochs . map ( ( entry ) => [ entry . graphId , entry ] ) ) ;
2294+ showSelectPicker (
2295+ 'Agent Graph History' ,
2296+ `${ items . length } run${ items . length === 1 ? '' : 's' } ` ,
2297+ items ,
2298+ ( item ) => {
2299+ const epoch = epochsByGraphId . get ( item . value ) ;
2300+ if ( ! epoch ) return ;
2301+ void runControl ( async ( ) => {
2302+ const graph = await input . agentGraphHistory ! . getSnapshot ( rootSessionId , epoch . graphId ) ;
2303+ state . entries . push ( {
2304+ kind : 'notice' ,
2305+ level : 'info' ,
2306+ text : formatAgentGraphHistory ( graph , epoch ) ,
2307+ } ) ;
2308+ requestRender ( ) ;
2309+ } ) ;
2310+ } ,
2311+ {
2312+ minPrimaryColumnWidth : 16 ,
2313+ maxPrimaryColumnWidth : 28 ,
2314+ selectedIndex : Math . max (
2315+ 0 ,
2316+ epochs . findIndex ( ( entry ) => entry . current ) ,
2317+ ) ,
2318+ hint : '↑↓ move · Enter inspect · Esc close' ,
2319+ } ,
2320+ ) ;
2321+ } ;
2322+
22672323 const setGraphMode = async ( mode : OrchestrationMode ) => {
22682324 if ( ! input . driver . setOrchestrationMode ) {
22692325 throw new Error ( 'Graph Mode is unavailable on this session driver.' ) ;
@@ -2283,6 +2339,10 @@ export async function runMakaPiTui(input: MakaPiTuiInput): Promise<void> {
22832339 showGraphStatus ( ) ;
22842340 return ;
22852341 }
2342+ if ( command . kind === 'history' ) {
2343+ void runControl ( showGraphHistory ) ;
2344+ return ;
2345+ }
22862346 if ( command . kind === 'set_mode' ) {
22872347 void runControl ( ( ) => setGraphMode ( command . mode ) ) ;
22882348 return ;
@@ -3025,6 +3085,41 @@ function formatContextCount(value: number): string {
30253085 return value . toLocaleString ( 'en-US' ) ;
30263086}
30273087
3088+ function formatAgentGraphHistory (
3089+ graph : AgentGraphClientSnapshot ,
3090+ epoch : AgentGraphEpochSummary ,
3091+ ) : string {
3092+ const settled = graph . operators . filter ( ( operator ) =>
3093+ [ 'completed' , 'failed' , 'aborted' , 'cancelled' ] . includes ( operator . status ) ,
3094+ ) . length ;
3095+ const lines = [
3096+ `Agent Graph run #${ epoch . epoch } ${ epoch . current ? ' · Current' : ' · History (read-only)' } ` ,
3097+ ` ${ formatAgentGraphStatus ( graph . status ) } · ${ settled } /${ graph . operators . length } operators settled` ,
3098+ ] ;
3099+ for ( const operator of graph . operators ) {
3100+ lines . push ( ` ${ operator . agentId } : ${ operator . status . replaceAll ( '_' , ' ' ) } ` ) ;
3101+ }
3102+ if ( graph . finish ) {
3103+ lines . push ( ` Selected results: ${ graph . finish . resultIds . join ( ', ' ) || 'none' } ` ) ;
3104+ }
3105+ if ( graph . omitted . operators > 0 ) {
3106+ lines . push ( ` ${ graph . omitted . operators } more operators omitted` ) ;
3107+ }
3108+ return lines . join ( '\n' ) ;
3109+ }
3110+
3111+ function formatAgentGraphStatus ( status : AgentGraphClientSnapshot [ 'status' ] ) : string {
3112+ return {
3113+ empty : 'Awaiting schedule' ,
3114+ active : 'Running' ,
3115+ closing : 'Finishing' ,
3116+ waiting : 'Waiting' ,
3117+ stopped : 'Stopped' ,
3118+ failed : 'Failed' ,
3119+ completed : 'Completed' ,
3120+ } [ status ] ;
3121+ }
3122+
30283123function flattenLinkedSessionTree (
30293124 roots : readonly SessionSummary [ ] ,
30303125 childrenByParentId : ReadonlyMap < string , readonly SessionSummary [ ] > ,
0 commit comments