@kal-elsam/kairo-runtime 0.2.3 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,44 +1,34 @@
1
- import React, { useEffect, useState } from "react";
1
+ import React, { useEffect, useReducer } from "react";
2
2
  import { Box, Text, useApp, useInput } from "ink";
3
- import { BRAND } from "../brand/index.js";
4
- import { buildReadOnlyDiagnostics } from "../action-planner.js";
5
- import { buildRuntimeDashboardData } from "../runtime/run-cli.js";
6
- import { readRunEvents } from "../runtime/run-store.js";
7
- import { stopRun } from "../runtime/run-manager.js";
8
- import { startRun } from "../runtime/run-manager.js";
9
3
  import {
10
- ORCHESTRATOR_MENU,
11
4
  ORCHESTRATOR_VIEWS,
12
- LAUNCH_WIZARD_STEPS,
13
- LAUNCH_PERMISSION_OPTIONS,
14
- createLaunchDraft,
15
- formatDashboardSnapshot,
16
- formatDiagnosticsLines,
17
- formatLaunchWizardLines,
18
- formatProviderLines,
19
- formatRunDetailLines,
20
- formatRunLines,
21
5
  isRunCancellable,
22
- resolveLaunchPermissions,
23
- resolveLaunchableAgents,
24
- resolveMenuItem,
25
- resolveMenuItemView,
26
- retreatLaunchWizardStep,
27
- selectRunFromList,
28
- shiftMenuIndex
6
+ selectRunFromList
29
7
  } from "./orchestrator-state.js";
30
8
  import {
31
- formatDashboardPurpose,
32
- resolveDashboardRecommendation
33
- } from "../dashboard-guidance.js";
34
-
35
- const COLORS = {
36
- accent: "cyan",
37
- success: "green",
38
- warning: "yellow",
39
- danger: "red",
40
- muted: "gray"
41
- };
9
+ createCockpitUiState,
10
+ reduceCockpitUi,
11
+ resolveNavAction,
12
+ routeCockpitKey,
13
+ isContentInteractiveView
14
+ } from "./cockpit-controller.js";
15
+ import {
16
+ COCKPIT_REGIONS,
17
+ buildFooterModel,
18
+ buildHomeMissionModel,
19
+ buildNavModel,
20
+ buildSystemStripModel,
21
+ buildTopBarModel,
22
+ resolveProjectName
23
+ } from "./cockpit-models.js";
24
+ import { CockpitShell } from "./cockpit/primitives.js";
25
+ import { renderCockpitView } from "./cockpit-views.js";
26
+ import { handleLaunchInput } from "./launch-input.js";
27
+ import { useTerminalSize } from "./use-terminal-size.js";
28
+ import { useOrchestratorData } from "./use-orchestrator-data.js";
29
+ import { resolveTerminalCapabilities } from "./terminal-capabilities.js";
30
+ import { COCKPIT_COLORS } from "./theme.js";
31
+ import { LAYOUT_MODES } from "./layout.js";
42
32
 
43
33
  export function OrchestratorApp({
44
34
  homeDir,
@@ -50,475 +40,231 @@ export function OrchestratorApp({
50
40
  onComplete
51
41
  }) {
52
42
  const { exit } = useApp();
53
- const [view, setView] = useState(ORCHESTRATOR_VIEWS.HOME);
54
- const [menuIndex, setMenuIndex] = useState(0);
55
- const [listIndex, setListIndex] = useState(0);
56
- const [launchAgentIndex, setLaunchAgentIndex] = useState(0);
57
- const [launchStep, setLaunchStep] = useState(LAUNCH_WIZARD_STEPS.AGENT);
58
- const [launchDraft, setLaunchDraft] = useState(createLaunchDraft);
59
- const [launchPermissionIndex, setLaunchPermissionIndex] = useState(0);
60
- const [loading, setLoading] = useState(true);
61
- const [busy, setBusy] = useState(false);
62
- const [error, setError] = useState(null);
63
- const [dashboard, setDashboard] = useState(null);
64
- const [diagnostics, setDiagnostics] = useState(null);
65
- const [selectedRun, setSelectedRun] = useState(null);
66
- const [selectedEvents, setSelectedEvents] = useState([]);
67
- const [statusMessage, setStatusMessage] = useState(null);
68
-
69
- const reload = async () => {
70
- const [dash, diag] = await Promise.all([
71
- buildRuntimeDashboardData({ homeDir, workspaceRoot, cliVersion }),
72
- buildReadOnlyDiagnostics({ homeDir, workspaceRoot, packageName, packageRoot, cliVersion })
73
- ]);
74
- setDashboard(dash);
75
- setDiagnostics(diag);
76
- };
43
+ const { columns, rows, layoutMode } = useTerminalSize();
44
+ const caps = resolveTerminalCapabilities({ columns, rows, isTTY: true });
45
+ const [ui, dispatch] = useReducer(
46
+ reduceCockpitUi,
47
+ createCockpitUiState({
48
+ layoutMode: layoutMode ?? LAYOUT_MODES.COMPACT,
49
+ region: COCKPIT_REGIONS.NAV
50
+ })
51
+ );
52
+ const data = useOrchestratorData({
53
+ homeDir,
54
+ workspaceRoot,
55
+ packageName,
56
+ packageRoot,
57
+ cliVersion
58
+ });
77
59
 
78
60
  useEffect(() => {
79
- let cancelled = false;
80
-
81
- async function load() {
82
- try {
83
- await reload();
84
- if (cancelled) return;
85
- setLoading(false);
86
- } catch (loadError) {
87
- if (cancelled) return;
88
- setError(loadError instanceof Error ? loadError.message : String(loadError));
89
- setLoading(false);
90
- }
91
- }
92
-
93
- load();
94
- return () => {
95
- cancelled = true;
96
- };
97
- }, [homeDir, workspaceRoot, packageName, packageRoot, cliVersion]);
61
+ if (layoutMode) dispatch({ type: "resize", layoutMode });
62
+ }, [layoutMode]);
98
63
 
99
64
  const finish = (outcome) => {
100
65
  onComplete(outcome);
101
66
  exit();
102
67
  };
103
68
 
104
- const openRunDetail = async (run) => {
105
- if (!run) return;
106
- const events = await readRunEvents(homeDir, run.runId, { limit: 20 });
107
- setSelectedRun(run);
108
- setSelectedEvents(events);
109
- setView(ORCHESTRATOR_VIEWS.RUN_DETAIL);
110
- };
111
-
112
- const launchableAgents = resolveLaunchableAgents(dashboard?.providers ?? []);
113
-
114
- const resetLaunchWizard = () => {
115
- setLaunchStep(LAUNCH_WIZARD_STEPS.AGENT);
116
- setLaunchDraft(createLaunchDraft());
117
- setLaunchAgentIndex(0);
118
- setLaunchPermissionIndex(0);
119
- };
120
-
121
- const handleLaunch = async (draft) => {
122
- if (!draft.agentId || !draft.task.trim()) {
123
- setError("Agent and task are required.");
124
- return;
125
- }
126
-
127
- setBusy(true);
128
- setStatusMessage(`Launching ${draft.agentId}…`);
129
- try {
130
- const permissions = resolveLaunchPermissions({
131
- ...draft,
132
- permissionIndex: launchPermissionIndex
133
- });
134
- const { runId } = await startRun({
135
- homeDir,
136
- agentId: draft.agentId,
137
- task: draft.task.trim(),
138
- cwd: workspaceRoot,
139
- model: draft.model.trim() || null,
140
- permissions,
141
- cliVersion,
142
- profile: dashboard?.profile ?? null,
143
- follow: false,
144
- wait: false
145
- });
146
- await reload();
147
- const run = (await buildRuntimeDashboardData({ homeDir, workspaceRoot, cliVersion }))
148
- .runs.find((entry) => entry.runId === runId);
149
- setStatusMessage(`Run started: ${runId}`);
150
- resetLaunchWizard();
151
- setView(ORCHESTRATOR_VIEWS.HOME);
152
- if (run) await openRunDetail(run);
153
- } catch (launchError) {
154
- setError(launchError instanceof Error ? launchError.message : String(launchError));
155
- } finally {
156
- setBusy(false);
157
- }
158
- };
159
-
160
- const handleCancelRun = async () => {
161
- if (!selectedRun || !isRunCancellable(selectedRun)) return;
162
- setBusy(true);
163
- try {
164
- await stopRun(homeDir, selectedRun.runId);
165
- await reload();
166
- const refreshed = await buildRuntimeDashboardData({ homeDir, workspaceRoot, cliVersion });
167
- const run = refreshed.runs.find((entry) => entry.runId === selectedRun.runId);
168
- const events = await readRunEvents(homeDir, selectedRun.runId, { limit: 20 });
169
- setSelectedRun(run ?? selectedRun);
170
- setSelectedEvents(events);
171
- setStatusMessage(`Cancelled ${selectedRun.runId}`);
172
- } catch (cancelError) {
173
- setError(cancelError instanceof Error ? cancelError.message : String(cancelError));
174
- } finally {
175
- setBusy(false);
176
- }
177
- };
178
-
179
69
  useInput((inputKey, key) => {
70
+ if (data.loading || data.error || data.busy) return;
71
+
180
72
  if (key.escape) {
181
- if (view === ORCHESTRATOR_VIEWS.HOME) {
73
+ // Launch wizard may retreat a step before leaving the view.
74
+ if (ui.view === ORCHESTRATOR_VIEWS.LAUNCH && data.launchableAgents.length > 0) {
75
+ const retreated = handleLaunchInput({
76
+ key,
77
+ inputKey,
78
+ launchStep: data.launchStep,
79
+ launchDraft: data.launchDraft,
80
+ launchableAgents: data.launchableAgents,
81
+ launchAgentIndex: data.launchAgentIndex,
82
+ launchPermissionIndex: data.launchPermissionIndex,
83
+ setLaunchAgentIndex: data.setLaunchAgentIndex,
84
+ setLaunchDraft: data.setLaunchDraft,
85
+ setLaunchStep: data.setLaunchStep,
86
+ setLaunchPermissionIndex: data.setLaunchPermissionIndex,
87
+ setError: data.setError,
88
+ handleLaunch: (draft) => data.handleLaunch(draft, data.dashboard?.profile, dispatch),
89
+ reload: data.reload,
90
+ allowEscapeRetreat: true
91
+ });
92
+ if (retreated === "retreated") return;
93
+ }
94
+
95
+ const next = reduceCockpitUi(ui, { type: "escape" });
96
+ if (next.shouldExit) {
182
97
  finish({ cancelled: true });
183
98
  return;
184
99
  }
185
- setView(ORCHESTRATOR_VIEWS.HOME);
186
- setSelectedRun(null);
187
- setListIndex(0);
100
+ data.setSelectedRun(null);
101
+ data.resetLaunchWizard();
102
+ dispatch({ type: "escape" });
188
103
  return;
189
104
  }
190
105
 
191
- if (loading || error || busy) return;
192
-
193
- if (view === ORCHESTRATOR_VIEWS.LAUNCH) {
194
- if (launchableAgents.length === 0) {
195
- if (key.escape) {
196
- resetLaunchWizard();
197
- setView(ORCHESTRATOR_VIEWS.HOME);
198
- }
199
- return;
200
- }
201
-
202
- if (launchStep === LAUNCH_WIZARD_STEPS.AGENT) {
203
- if (key.upArrow) {
204
- setLaunchAgentIndex((index) => Math.max(0, index - 1));
205
- return;
206
- }
207
- if (key.downArrow) {
208
- setLaunchAgentIndex((index) => Math.min(launchableAgents.length - 1, index + 1));
209
- return;
210
- }
211
- if (key.return) {
212
- const agentId = launchableAgents[launchAgentIndex];
213
- setLaunchDraft((draft) => ({ ...draft, agentId }));
214
- setLaunchStep(LAUNCH_WIZARD_STEPS.TASK);
215
- }
216
- return;
217
- }
218
-
219
- if (launchStep === LAUNCH_WIZARD_STEPS.TASK) {
220
- if (key.return) {
221
- if (!launchDraft.task.trim()) {
222
- setError("Task cannot be empty.");
223
- return;
224
- }
225
- setLaunchStep(LAUNCH_WIZARD_STEPS.MODEL);
226
- return;
227
- }
228
- if (key.backspace || key.delete) {
229
- setLaunchDraft((draft) => ({ ...draft, task: draft.task.slice(0, -1) }));
230
- return;
231
- }
232
- if (inputKey && inputKey.length === 1 && !key.ctrl && !key.meta) {
233
- setLaunchDraft((draft) => ({ ...draft, task: `${draft.task}${inputKey}` }));
234
- }
235
- return;
236
- }
106
+ if (inputKey === "?") {
107
+ dispatch({ type: "toggle-help" });
108
+ return;
109
+ }
237
110
 
238
- if (launchStep === LAUNCH_WIZARD_STEPS.MODEL) {
239
- if (key.return) {
240
- setLaunchStep(LAUNCH_WIZARD_STEPS.PERMISSIONS);
241
- return;
242
- }
243
- if (key.backspace || key.delete) {
244
- setLaunchDraft((draft) => ({ ...draft, model: draft.model.slice(0, -1) }));
245
- return;
246
- }
247
- if (inputKey && inputKey.length === 1 && !key.ctrl && !key.meta) {
248
- setLaunchDraft((draft) => ({ ...draft, model: `${draft.model}${inputKey}` }));
249
- }
250
- return;
251
- }
111
+ const listLength = ui.view === ORCHESTRATOR_VIEWS.ACTIVE_RUNS
112
+ ? (data.dashboard?.activeRuns ?? []).length
113
+ : ui.view === ORCHESTRATOR_VIEWS.RECENT_RUNS
114
+ ? (data.dashboard?.recentRuns ?? []).length
115
+ : 0;
116
+
117
+ let routed = null;
118
+ if (key.tab) {
119
+ routed = routeCockpitKey(ui, { type: "tab" });
120
+ } else if (key.upArrow || key.downArrow) {
121
+ routed = routeCockpitKey(ui, {
122
+ type: "arrow",
123
+ direction: key.upArrow ? "up" : "down",
124
+ listLength
125
+ });
126
+ } else if (key.return) {
127
+ routed = routeCockpitKey(ui, { type: "enter" });
128
+ }
252
129
 
253
- if (launchStep === LAUNCH_WIZARD_STEPS.PERMISSIONS) {
254
- if (key.upArrow) {
255
- setLaunchPermissionIndex((index) => Math.max(0, index - 1));
256
- return;
257
- }
258
- if (key.downArrow) {
259
- setLaunchPermissionIndex((index) => Math.min(LAUNCH_PERMISSION_OPTIONS.length - 1, index + 1));
130
+ if (routed) {
131
+ if (routed.type === "enter-nav") {
132
+ const item = resolveNavAction(ui.navIndex);
133
+ if (item?.action === "launch") {
134
+ data.resetLaunchWizard();
135
+ dispatch({ type: "set-view", view: ORCHESTRATOR_VIEWS.LAUNCH });
260
136
  return;
261
137
  }
262
- if (key.return) {
263
- setLaunchStep(LAUNCH_WIZARD_STEPS.CONFIRM);
264
- }
265
- return;
266
- }
267
-
268
- if (launchStep === LAUNCH_WIZARD_STEPS.CONFIRM) {
269
- if (key.return) {
270
- handleLaunch({ ...launchDraft, permissionIndex: launchPermissionIndex });
271
- }
272
- if (key.escape) {
273
- setLaunchStep(retreatLaunchWizardStep(launchStep));
274
- }
275
- return;
276
- }
277
-
278
- if (inputKey.toLowerCase() === "r") {
279
- reload().catch((reloadError) => {
280
- setError(reloadError instanceof Error ? reloadError.message : String(reloadError));
281
- });
282
138
  }
139
+ dispatch(routed);
283
140
  return;
284
141
  }
285
142
 
286
- if (view === ORCHESTRATOR_VIEWS.ACTIVE_RUNS || view === ORCHESTRATOR_VIEWS.RECENT_RUNS) {
287
- const runs = view === ORCHESTRATOR_VIEWS.ACTIVE_RUNS
288
- ? dashboard?.activeRuns ?? []
289
- : dashboard?.recentRuns ?? [];
290
-
291
- if (key.upArrow) {
292
- setListIndex((index) => Math.max(0, index - 1));
293
- return;
294
- }
295
- if (key.downArrow) {
296
- setListIndex((index) => Math.min(Math.max(runs.length - 1, 0), index + 1));
143
+ // View-specific handlers after centralized region routing.
144
+ if (ui.view === ORCHESTRATOR_VIEWS.LAUNCH && data.launchableAgents.length > 0) {
145
+ if (handleLaunchInput({
146
+ key,
147
+ inputKey,
148
+ launchStep: data.launchStep,
149
+ launchDraft: data.launchDraft,
150
+ launchableAgents: data.launchableAgents,
151
+ launchAgentIndex: data.launchAgentIndex,
152
+ launchPermissionIndex: data.launchPermissionIndex,
153
+ setLaunchAgentIndex: data.setLaunchAgentIndex,
154
+ setLaunchDraft: data.setLaunchDraft,
155
+ setLaunchStep: data.setLaunchStep,
156
+ setLaunchPermissionIndex: data.setLaunchPermissionIndex,
157
+ setError: data.setError,
158
+ handleLaunch: (draft) => data.handleLaunch(draft, data.dashboard?.profile, dispatch),
159
+ reload: data.reload
160
+ })) {
297
161
  return;
298
162
  }
299
- if (key.return) {
300
- openRunDetail(selectRunFromList(runs, listIndex));
301
- }
302
- if (inputKey.toLowerCase() === "r") {
303
- reload().catch((reloadError) => {
304
- setError(reloadError instanceof Error ? reloadError.message : String(reloadError));
305
- });
306
- }
307
- return;
308
163
  }
309
164
 
310
- if (view === ORCHESTRATOR_VIEWS.RUN_DETAIL) {
311
- if (inputKey.toLowerCase() === "c" && isRunCancellable(selectedRun)) {
312
- handleCancelRun();
313
- }
314
- if (inputKey.toLowerCase() === "r") {
315
- openRunDetail(selectedRun);
316
- }
165
+ if (ui.region === COCKPIT_REGIONS.CONTENT
166
+ && (ui.view === ORCHESTRATOR_VIEWS.ACTIVE_RUNS || ui.view === ORCHESTRATOR_VIEWS.RECENT_RUNS)
167
+ && key.return) {
168
+ const runs = ui.view === ORCHESTRATOR_VIEWS.ACTIVE_RUNS
169
+ ? data.dashboard?.activeRuns ?? []
170
+ : data.dashboard?.recentRuns ?? [];
171
+ data.openRunDetail(selectRunFromList(runs, ui.listIndex), dispatch, ui.view);
317
172
  return;
318
173
  }
319
174
 
320
- if (view !== ORCHESTRATOR_VIEWS.HOME) {
175
+ if (ui.view === ORCHESTRATOR_VIEWS.RUN_DETAIL) {
176
+ if (inputKey.toLowerCase() === "c" && isRunCancellable(data.selectedRun)) {
177
+ data.handleCancelRun();
178
+ return;
179
+ }
321
180
  if (inputKey.toLowerCase() === "r") {
322
- reload().catch((reloadError) => {
323
- setError(reloadError instanceof Error ? reloadError.message : String(reloadError));
324
- });
181
+ data.openRunDetail(data.selectedRun, dispatch, ui.returnView);
182
+ return;
325
183
  }
326
- return;
327
- }
328
-
329
- if (key.upArrow) {
330
- setMenuIndex((index) => shiftMenuIndex(index, "up"));
331
- return;
332
184
  }
333
185
 
334
- if (key.downArrow) {
335
- setMenuIndex((index) => shiftMenuIndex(index, "down"));
336
- return;
337
- }
338
-
339
- if (!key.return) return;
340
-
341
- const item = resolveMenuItem(menuIndex);
342
- if (item?.action === "launch") {
343
- resetLaunchWizard();
344
- setView(ORCHESTRATOR_VIEWS.LAUNCH);
345
- return;
186
+ if (inputKey.toLowerCase() === "r" && ui.view !== ORCHESTRATOR_VIEWS.LAUNCH) {
187
+ data.reload().catch((reloadError) => {
188
+ data.setError(reloadError instanceof Error ? reloadError.message : String(reloadError));
189
+ });
346
190
  }
347
-
348
- setView(resolveMenuItemView(menuIndex));
349
- setListIndex(0);
350
191
  });
351
192
 
352
- if (loading) {
193
+ if (data.loading) {
353
194
  return React.createElement(Box, { flexDirection: "column" },
354
- React.createElement(Text, { bold: true, color: COLORS.accent }, BRAND.displayName),
355
- React.createElement(Text, { color: COLORS.muted }, "Loading runtime dashboard…")
195
+ React.createElement(Text, { bold: true, color: COCKPIT_COLORS.primary }, "KAIRO"),
196
+ React.createElement(Text, { color: COCKPIT_COLORS.muted }, "Loading cockpit…")
356
197
  );
357
198
  }
358
199
 
359
- if (error) {
200
+ if (data.error) {
360
201
  return React.createElement(Box, { flexDirection: "column" },
361
- React.createElement(Text, { bold: true, color: COLORS.danger }, "Runtime error"),
362
- React.createElement(Text, null, error),
202
+ React.createElement(Text, { bold: true, color: COCKPIT_COLORS.danger }, "Runtime error"),
203
+ React.createElement(Text, null, data.error),
363
204
  React.createElement(Text, { dimColor: true }, "Esc to exit")
364
205
  );
365
206
  }
366
207
 
208
+ const mode = ui.layoutMode ?? LAYOUT_MODES.COMPACT;
209
+ const colorEnabled = caps.color;
210
+ const unicode = caps.unicode;
211
+
367
212
  return React.createElement(Box, { flexDirection: "column" },
368
- React.createElement(Text, { bold: true, color: COLORS.accent }, `${BRAND.displayName} runtime`),
369
- React.createElement(Text, { color: COLORS.muted }, formatDashboardPurpose()),
370
- statusMessage && React.createElement(Text, { color: COLORS.success }, statusMessage),
371
- React.createElement(Text, null, ""),
372
- renderView({
373
- view,
374
- dashboard,
375
- diagnostics,
376
- hasGlobalState,
377
- menuIndex,
378
- listIndex,
379
- launchStep,
380
- launchDraft,
381
- launchAgentIndex,
382
- launchPermissionIndex,
383
- launchableAgents,
384
- selectedRun,
385
- selectedEvents
386
- }),
387
- React.createElement(Text, null, ""),
388
- React.createElement(Text, { dimColor: true }, footerHint(view, selectedRun, launchStep))
213
+ data.statusMessage && React.createElement(Text, {
214
+ color: COCKPIT_COLORS.success
215
+ }, data.statusMessage),
216
+ React.createElement(CockpitShell, {
217
+ topBar: buildTopBarModel({
218
+ projectName: resolveProjectName(workspaceRoot),
219
+ systemOnline: true,
220
+ unicode
221
+ }),
222
+ footer: buildFooterModel({
223
+ view: ui.view,
224
+ region: ui.region,
225
+ helpOpen: ui.helpOpen,
226
+ canCancel: isRunCancellable(data.selectedRun),
227
+ unicode
228
+ }),
229
+ layoutMode: mode,
230
+ nav: buildNavModel({
231
+ navIndex: ui.navIndex,
232
+ focused: ui.region === COCKPIT_REGIONS.NAV || !isContentInteractiveView(ui.view),
233
+ unicode
234
+ }),
235
+ system: buildSystemStripModel({
236
+ dashboard: data.dashboard,
237
+ diagnostics: data.diagnostics,
238
+ healthKind: "ready"
239
+ }),
240
+ navFocused: ui.region === COCKPIT_REGIONS.NAV,
241
+ contentFocused: ui.region === COCKPIT_REGIONS.CONTENT,
242
+ systemFocused: ui.region === COCKPIT_REGIONS.SYSTEM,
243
+ colorEnabled
244
+ },
245
+ renderCockpitView({
246
+ view: ui.view,
247
+ dashboard: data.dashboard,
248
+ diagnostics: data.diagnostics,
249
+ listIndex: ui.listIndex,
250
+ launchStep: data.launchStep,
251
+ launchDraft: data.launchDraft,
252
+ launchAgentIndex: data.launchAgentIndex,
253
+ launchPermissionIndex: data.launchPermissionIndex,
254
+ launchableAgents: data.launchableAgents,
255
+ selectedRun: data.selectedRun,
256
+ selectedEvents: data.selectedEvents,
257
+ homeMission: buildHomeMissionModel({
258
+ hasGlobalState,
259
+ diagnostics: data.diagnostics,
260
+ dashboard: data.dashboard,
261
+ layoutMode: mode,
262
+ activityLines: (data.dashboard?.recentRuns ?? []).map((run) =>
263
+ `${run.runId} ${run.state} ${run.agentId}`
264
+ )
265
+ }),
266
+ colorEnabled
267
+ })
268
+ )
389
269
  );
390
270
  }
391
-
392
- function renderView({
393
- view,
394
- dashboard,
395
- diagnostics,
396
- hasGlobalState,
397
- menuIndex,
398
- listIndex,
399
- launchStep,
400
- launchDraft,
401
- launchAgentIndex,
402
- launchPermissionIndex,
403
- launchableAgents,
404
- selectedRun,
405
- selectedEvents
406
- }) {
407
- switch (view) {
408
- case ORCHESTRATOR_VIEWS.HOME: {
409
- const nextStep = resolveDashboardRecommendation({
410
- hasGlobalState,
411
- diagnostics,
412
- dashboard
413
- });
414
- return React.createElement(Box, { flexDirection: "column" },
415
- React.createElement(Text, { bold: true }, "Next"),
416
- React.createElement(Text, { color: COLORS.accent }, nextStep.message),
417
- React.createElement(Text, null, ""),
418
- React.createElement(Text, { bold: true }, "Operations"),
419
- ORCHESTRATOR_MENU.map((item, index) =>
420
- React.createElement(Text, {
421
- key: item.id,
422
- color: index === menuIndex ? COLORS.accent : undefined,
423
- bold: index === menuIndex
424
- }, `${index === menuIndex ? "› " : " "}${item.label}`)
425
- ),
426
- React.createElement(Text, null, ""),
427
- React.createElement(Text, { bold: true }, "Snapshot"),
428
- formatDashboardSnapshot(dashboard)
429
- .map((line) => React.createElement(Text, { key: line }, line))
430
- );
431
- }
432
- case ORCHESTRATOR_VIEWS.ACTIVE_RUNS:
433
- return React.createElement(Box, { flexDirection: "column" },
434
- React.createElement(Text, { bold: true }, "Active runs"),
435
- formatRunLines(dashboard?.activeRuns ?? [], { emptyMessage: "No active runs." })
436
- .map((line, index) => React.createElement(Text, {
437
- key: line,
438
- color: index === listIndex ? COLORS.accent : undefined,
439
- bold: index === listIndex
440
- }, `${index === listIndex ? "› " : " "}${line}`))
441
- );
442
- case ORCHESTRATOR_VIEWS.RECENT_RUNS:
443
- return React.createElement(Box, { flexDirection: "column" },
444
- React.createElement(Text, { bold: true }, "Recent runs"),
445
- formatRunLines(dashboard?.recentRuns ?? [], { emptyMessage: "No completed runs yet." })
446
- .map((line, index) => React.createElement(Text, {
447
- key: line,
448
- color: index === listIndex ? COLORS.accent : undefined,
449
- bold: index === listIndex
450
- }, `${index === listIndex ? "› " : " "}${line}`))
451
- );
452
- case ORCHESTRATOR_VIEWS.PROVIDERS:
453
- return React.createElement(Box, { flexDirection: "column" },
454
- React.createElement(Text, { bold: true }, "Providers"),
455
- formatProviderLines(dashboard?.providers ?? [])
456
- .map((line) => React.createElement(Text, { key: line }, line))
457
- );
458
- case ORCHESTRATOR_VIEWS.LAUNCH:
459
- if (launchableAgents.length === 0) {
460
- return React.createElement(Box, { flexDirection: "column" },
461
- React.createElement(Text, { bold: true }, "Launch run"),
462
- React.createElement(Text, { color: COLORS.warning }, "No launchable agents detected."),
463
- React.createElement(Text, { dimColor: true }, "Esc to return")
464
- );
465
- }
466
- return React.createElement(Box, { flexDirection: "column" },
467
- React.createElement(Text, { bold: true }, "Launch run"),
468
- formatLaunchWizardLines({
469
- step: launchStep,
470
- draft: launchDraft,
471
- launchableAgents,
472
- agentIndex: launchAgentIndex,
473
- permissionIndex: launchPermissionIndex
474
- }).map((line) => React.createElement(Text, { key: line, color: line.startsWith("›") ? COLORS.accent : undefined }, line))
475
- );
476
- case ORCHESTRATOR_VIEWS.RUN_DETAIL:
477
- return React.createElement(Box, { flexDirection: "column" },
478
- React.createElement(Text, { bold: true }, "Run detail"),
479
- formatRunDetailLines(selectedRun, selectedEvents)
480
- .map((line) => React.createElement(Text, { key: line }, line))
481
- );
482
- case ORCHESTRATOR_VIEWS.DIAGNOSTICS:
483
- return React.createElement(Box, { flexDirection: "column" },
484
- React.createElement(Text, { bold: true }, "Diagnostics"),
485
- formatDiagnosticsLines(diagnostics)
486
- .map((line) => React.createElement(Text, { key: line }, line))
487
- );
488
- case ORCHESTRATOR_VIEWS.HELP:
489
- return React.createElement(Box, { flexDirection: "column" },
490
- React.createElement(Text, { bold: true }, "Help"),
491
- React.createElement(Text, null, "Kairo runtime launches and audits agent CLIs you manage."),
492
- React.createElement(Text, null, "CLI: kairo run --agent <id> --task \"...\""),
493
- React.createElement(Text, null, "CLI: kairo runs list|show|stop"),
494
- React.createElement(Text, null, "Audit trail: ~/.harness/runs/<runId>/"),
495
- React.createElement(Text, null, "Transcripts are opt-in via --capture-transcript."),
496
- React.createElement(Text, null, "Credentials stay in environment variables — never in profiles.")
497
- );
498
- default: {
499
- const _exhaustive = view;
500
- return React.createElement(Text, null, `Unknown view: ${_exhaustive}`);
501
- }
502
- }
503
- }
504
-
505
- function footerHint(view, selectedRun, launchStep) {
506
- if (view === ORCHESTRATOR_VIEWS.HOME) return "↑↓ navigate · Enter select · Esc quit";
507
- if (view === ORCHESTRATOR_VIEWS.ACTIVE_RUNS || view === ORCHESTRATOR_VIEWS.RECENT_RUNS) {
508
- return "↑↓ select run · Enter inspect · R refresh · Esc menu";
509
- }
510
- if (view === ORCHESTRATOR_VIEWS.LAUNCH) {
511
- if (launchStep === LAUNCH_WIZARD_STEPS.TASK || launchStep === LAUNCH_WIZARD_STEPS.MODEL) {
512
- return "Type · Enter next · Esc menu";
513
- }
514
- if (launchStep === LAUNCH_WIZARD_STEPS.CONFIRM) {
515
- return "Enter launch · Esc back · R refresh";
516
- }
517
- return "↑↓ navigate · Enter select · Esc menu";
518
- }
519
- if (view === ORCHESTRATOR_VIEWS.RUN_DETAIL) {
520
- if (isRunCancellable(selectedRun)) return "C cancel · R refresh · Esc menu";
521
- return "R refresh · Esc menu";
522
- }
523
- return "R refresh · Esc menu";
524
- }