Skip to content

Commit d5dc926

Browse files
committed
feat(workflow): add agent engine tracking and update functionality
- Introduce new agent:engine event type and emitter method - Extend UI state and actions to handle engine updates - Update workflow execution to track and display resolved engines
1 parent 5d6acfb commit d5dc926

File tree

6 files changed

+53
-0
lines changed

6 files changed

+53
-0
lines changed

src/cli/tui/routes/workflow/adapters/opentui.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ export interface UIActions {
3030
totalSteps?: number
3131
}): void
3232
updateAgentStatus(agentId: string, status: AgentStatus): void
33+
updateAgentEngine(agentId: string, engine: string): void
3334
updateAgentModel(agentId: string, model: string): void
3435
updateAgentTelemetry(
3536
agentId: string,
@@ -124,6 +125,10 @@ export class OpenTUIAdapter extends BaseUIAdapter {
124125
this.actions.updateAgentStatus(event.agentId, event.status)
125126
break
126127

128+
case "agent:engine":
129+
this.actions.updateAgentEngine(event.agentId, event.engine)
130+
break
131+
127132
case "agent:model":
128133
this.actions.updateAgentModel(event.agentId, event.model)
129134
break

src/cli/tui/routes/workflow/context/ui-state/actions/agent-actions.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,17 @@ export function createAgentActions(ctx: AgentActionsContext) {
4545
ctx.notifyImmediate()
4646
}
4747

48+
function updateAgentEngine(agentId: string, engine: string): void {
49+
const state = ctx.getState()
50+
ctx.setState({
51+
...state,
52+
agents: state.agents.map((agent) =>
53+
agent.id === agentId ? { ...agent, engine } : agent,
54+
),
55+
})
56+
ctx.notify()
57+
}
58+
4859
function updateAgentModel(agentId: string, model: string): void {
4960
const state = ctx.getState()
5061
ctx.setState({
@@ -80,6 +91,7 @@ export function createAgentActions(ctx: AgentActionsContext) {
8091
return {
8192
addAgent,
8293
updateAgentStatus,
94+
updateAgentEngine,
8395
updateAgentModel,
8496
updateAgentTelemetry,
8597
registerMonitoringId,

src/cli/tui/routes/workflow/context/ui-state/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ export type UIActions = {
2020
subscribe(fn: Listener): () => void
2121
addAgent(agent: WorkflowState["agents"][number]): void
2222
updateAgentStatus(agentId: string, status: AgentStatus): void
23+
updateAgentEngine(agentId: string, engine: string): void
2324
updateAgentModel(agentId: string, model: string): void
2425
updateAgentTelemetry(agentId: string, telemetry: Partial<WorkflowState["agents"][number]["telemetry"]>): void
2526
setLoopState(loopState: LoopState | null): void

src/workflows/events/emitter.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,17 @@ export class WorkflowEventEmitter {
139139
});
140140
}
141141

142+
/**
143+
* Emit agent engine update (called when engine is resolved at execution time)
144+
*/
145+
updateAgentEngine(agentId: string, engine: string): void {
146+
this.bus.emit({
147+
type: 'agent:engine',
148+
agentId,
149+
engine,
150+
});
151+
}
152+
142153
/**
143154
* Emit agent model update (called when model is resolved at execution time)
144155
*/

src/workflows/events/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ export type WorkflowEvent =
4646
// Agent lifecycle events
4747
| { type: 'agent:added'; agent: AgentInfo }
4848
| { type: 'agent:status'; agentId: string; status: AgentStatus }
49+
| { type: 'agent:engine'; agentId: string; engine: string }
4950
| { type: 'agent:model'; agentId: string; model: string }
5051
| { type: 'agent:telemetry'; agentId: string; telemetry: Partial<AgentTelemetry> }
5152
| { type: 'agent:reset'; agentId: string; cycleNumber?: number }

src/workflows/execution/run.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ import {
1515
getSelectedTrack,
1616
getSelectedConditions,
1717
getStepData,
18+
loadControllerConfig,
19+
setAutonomousMode,
1820
} from '../../shared/workflows/index.js';
1921
import { registry } from '../../infra/engines/index.js';
2022
import { shouldSkipStep, logSkipDebug, type ActiveLoop } from '../behaviors/skip.js';
@@ -89,6 +91,11 @@ export async function runWorkflow(options: RunWorkflowOptions = {}): Promise<voi
8991
// Load selected conditions for condition-based filtering
9092
const selectedConditions = await getSelectedConditions(cmRoot);
9193

94+
// Load controller configuration (autonomous mode)
95+
const controllerState = await loadControllerConfig(cmRoot);
96+
const controllerConfig = controllerState.controllerConfig ?? null;
97+
let controllerEnabled = template.controller === true && controllerState.autonomousMode === true && !!controllerConfig;
98+
9299
// Load chain resume info (for resuming mid-chain)
93100
const chainResumeInfo = await getChainResumeInfo(cmRoot);
94101

@@ -236,6 +243,13 @@ export async function runWorkflow(options: RunWorkflowOptions = {}): Promise<voi
236243

237244
process.on('workflow:pause', pauseListener);
238245
process.on('workflow:input', inputListener);
246+
const autonomyListener = () => {
247+
controllerEnabled = false;
248+
setAutonomousMode(cmRoot, false).catch(() => {
249+
// best-effort persistence; keep running even if it fails
250+
});
251+
};
252+
process.on('workflow:autonomy:disable', autonomyListener);
239253

240254
try {
241255
debug(`[DEBUG workflow] Entering step loop. startIndex=${startIndex}, totalSteps=${template.steps.length}, workflowShouldStop=${workflowShouldStop}`);
@@ -336,6 +350,9 @@ export async function runWorkflow(options: RunWorkflowOptions = {}): Promise<voi
336350
// Mutate current step to carry the chosen engine forward
337351
step.engine = engineType;
338352

353+
// Update TUI with resolved engine (may differ from workflow file due to fallback)
354+
emitter.updateAgentEngine(uniqueAgentId, engineType);
355+
339356
debug(`[DEBUG workflow] Resolving model...`);
340357
// Resolve model: step override > engine default, and update UI
341358
const engineModule = registry.get(engineType);
@@ -406,6 +423,11 @@ export async function runWorkflow(options: RunWorkflowOptions = {}): Promise<voi
406423
isResumingFromSavedSessionWithChains,
407424
stepResumePrompt,
408425
stepResumeMonitoringId,
426+
controller: controllerEnabled && controllerConfig ? {
427+
isEnabled: () => controllerEnabled,
428+
agentId: controllerConfig.agentId,
429+
sessionId: controllerConfig.sessionId,
430+
} : undefined,
409431
});
410432

411433
// Handle post-execution behaviors
@@ -541,5 +563,6 @@ export async function runWorkflow(options: RunWorkflowOptions = {}): Promise<voi
541563
process.removeListener('workflow:stop', stopListener);
542564
process.removeListener('workflow:pause', pauseListener);
543565
process.removeListener('workflow:input', inputListener);
566+
process.removeListener('workflow:autonomy:disable', autonomyListener);
544567
}
545568
}

0 commit comments

Comments
 (0)