@kal-elsam/kairo-runtime 0.3.1 → 0.4.0

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.
package/README.md CHANGED
@@ -35,19 +35,22 @@ kairo
35
35
  **First run** (no `~/.harness/state.json`): interactive onboarding → safe diagnosis →
36
36
  setup with confirmation → full-screen operations cockpit.
37
37
 
38
- **Later runs** (state present): full-screen cockpit with mission control (recommended
39
- action), navigation, and a system strip. Layout adapts to terminal size:
38
+ **Later runs** (state present): full-screen cockpit Home that explains what Kairo
39
+ does, shows real readiness, and recommends a useful next action. Layout adapts to
40
+ terminal size:
40
41
 
41
42
  | Mode | Size | Layout |
42
43
  |------|------|--------|
43
- | Wide | ≥100 cols × ≥28 rows | Nav + content + system |
44
- | Compact | ≥72×20 | Nav + content |
45
- | Minimal | 60–71 cols or short height | Single panel + nav header |
44
+ | Wide | ≥100 cols × ≥28 rows | Nav + Home/content + system |
45
+ | Compact | ≥72×20 | Nav + Home/content (readiness embedded) |
46
+ | Minimal | 60–71 cols or short height | Selected section + essential readiness/next/recent |
46
47
  | Below gate | <60 cols | Explicit TTY fallback (Ink disabled) |
47
48
 
48
- Keys: `↑↓` navigate · `Enter` open · `Esc` back (exit only from Overview) ·
49
- `R` refresh · `C` cancel run · `?` help. `Tab` switches region only when content
50
- is interactive (run lists / launch).
49
+ Keys: `↑↓` navigate (selection explanation updates) · `Enter` open ·
50
+ `Esc` back (exit only from Home) · `R` refresh/retry · `C` cancel run · `?` help.
51
+ `Tab` switches region only when content is interactive (run lists / New run).
52
+
53
+ Navigation labels: Home · Running now · History · Agents · New run · System health.
51
54
 
52
55
  Respects `NO_COLOR`, `HARNESS_ASCII=1`, and `HARNESS_INK=0`. Status is always labeled
53
56
  in text, never color alone.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kal-elsam/kairo-runtime",
3
- "version": "0.3.1",
3
+ "version": "0.4.0",
4
4
  "description": "Kairo Runtime — local agent operating system for Codex, Cursor, Claude, Gemini, Copilot, Engram, and Graphify.",
5
5
  "type": "module",
6
6
  "homepage": "https://github.com/Kal-elSam/harness#readme",
@@ -9,6 +9,7 @@ import { createFullscreenSession } from "../src/global/ink/fullscreen-session.js
9
9
  import {
10
10
  buildFooterModel,
11
11
  buildHomeMissionModel,
12
+ buildNavModel,
12
13
  buildTopBarModel,
13
14
  COCKPIT_NAV,
14
15
  COCKPIT_REGIONS
@@ -24,7 +25,7 @@ const root = join(dirname(fileURLToPath(import.meta.url)), "..");
24
25
  const require = createRequire(import.meta.url);
25
26
  const pkg = require(join(root, "package.json"));
26
27
 
27
- assert.equal(pkg.version, "0.3.1");
28
+ assert.equal(pkg.version, "0.4.0");
28
29
  assert.ok(pkg.dependencies["ansi-escapes"]);
29
30
 
30
31
  assert.equal(resolveLayoutMode({ columns: 120, rows: 40 }), LAYOUT_MODES.WIDE);
@@ -51,14 +52,39 @@ assert.equal(session.enter(), true);
51
52
  assert.equal(session.leave(), true);
52
53
  assert.equal(session.leave(), false);
53
54
 
54
- const top = buildTopBarModel({ projectName: "smoke" });
55
+ const top = buildTopBarModel({ projectName: "smoke", unicode: false });
55
56
  assert.match(top.status, /ONLINE|Offline/);
56
57
  const mission = buildHomeMissionModel({
57
- hasGlobalState: false,
58
- diagnostics: { diagnostics: { detected: 0 } },
59
- dashboard: { providers: [], recentRuns: [] }
58
+ projectName: "smoke",
59
+ hasGlobalState: true,
60
+ diagnostics: {
61
+ diagnostics: { detected: 2, errors: 0 },
62
+ intelligence: { summary: { localAvailable: false, cloudAuthenticated: false } },
63
+ recommendations: []
64
+ },
65
+ dashboard: {
66
+ providers: [{ launchable: true }],
67
+ recentRuns: [{ runId: "r1", agentId: "codex", state: "failed" }]
68
+ },
69
+ layoutMode: LAYOUT_MODES.MINIMAL
60
70
  });
61
- assert.match(mission.title, /MISSION CONTROL/);
71
+ assert.match(mission.title, /HOME — smoke/);
72
+ assert.match(mission.readiness.headline, /LIMITED|READY|NEEDS/i);
73
+ assert.equal(mission.next.targetAction, "launch");
74
+ assert.match(mission.recent.headline, /Codex · Failed/);
75
+
76
+ const asciiNav = buildNavModel({
77
+ navIndex: 1,
78
+ currentView: ORCHESTRATOR_VIEWS.HOME,
79
+ focused: true,
80
+ unicode: false,
81
+ dashboard: { activeRuns: [], recentRuns: [], providers: [{ launchable: true }] },
82
+ diagnostics: { diagnostics: { detected: 1, errors: 0 } }
83
+ });
84
+ assert.equal(asciiNav.items[1].marker, ">");
85
+ assert.equal(asciiNav.items[0].current, true);
86
+ assert.equal(asciiNav.items[1].selected, true);
87
+ assert.match(asciiNav.explanation, /Supervised runs|Running/i);
62
88
 
63
89
  function applyKey(state, keyAction) {
64
90
  const routed = routeCockpitKey(state, keyAction);
@@ -1,7 +1,15 @@
1
1
  import { formatCliCommand } from "./brand/cli.js";
2
2
 
3
+ /** Public cockpit destinations for recommended actions (match ORCHESTRATOR_VIEWS). */
4
+ export const RECOMMENDATION_TARGETS = {
5
+ DIAGNOSTICS: "diagnostics",
6
+ LAUNCH: "launch",
7
+ RECENT_RUNS: "recent-runs",
8
+ PROVIDERS: "providers"
9
+ };
10
+
3
11
  export const DASHBOARD_PURPOSE =
4
- "Detects, configures, and coordinates local AI agents no changes without confirmation.";
12
+ "Kairo coordinates installed AI agents for this project, with controlled execution and auditable results.";
5
13
 
6
14
  export const NEXT_STEP_KINDS = {
7
15
  CONFIGURE: "configure",
@@ -10,13 +18,117 @@ export const NEXT_STEP_KINDS = {
10
18
  REVIEW: "review"
11
19
  };
12
20
 
21
+ export const READINESS_KINDS = {
22
+ NEEDS_SETUP: "needs_setup",
23
+ NEEDS_ATTENTION: "needs_attention",
24
+ LIMITED: "limited",
25
+ READY: "ready"
26
+ };
27
+
28
+ export const READINESS_LABELS = {
29
+ [READINESS_KINDS.NEEDS_SETUP]: "Needs setup",
30
+ [READINESS_KINDS.NEEDS_ATTENTION]: "Needs attention",
31
+ [READINESS_KINDS.LIMITED]: "Limited",
32
+ [READINESS_KINDS.READY]: "Ready to work"
33
+ };
34
+
13
35
  export function formatDashboardPurpose() {
14
36
  return DASHBOARD_PURPOSE;
15
37
  }
16
38
 
39
+ export function countLaunchableAgents(dashboard = null) {
40
+ return (dashboard?.providers ?? []).filter((entry) => entry.launchable).length;
41
+ }
42
+
43
+ export function hasIntelligenceConfigured(diagnostics = null) {
44
+ const summary = diagnostics?.intelligence?.summary;
45
+ return Boolean(summary?.localAvailable || summary?.cloudAuthenticated);
46
+ }
47
+
48
+ function hasBlockingDiagnostics(diagnostics = null) {
49
+ const summary = diagnostics?.diagnostics ?? { errors: 0 };
50
+ const hasErrors = (summary.errors ?? 0) > 0;
51
+ const hasProblemRecommendation = (diagnostics?.recommendations ?? []).some((line) =>
52
+ /error|fix|drift|not detected|failed|problem/i.test(line)
53
+ );
54
+ return hasErrors || hasProblemRecommendation;
55
+ }
56
+
57
+ /**
58
+ * Derive project readiness from diagnostics + dashboard.
59
+ * Intelligence absence is Limited (optional), never Needs attention by itself.
60
+ */
61
+ export function resolveProjectReadiness({
62
+ hasGlobalState = false,
63
+ diagnostics = null,
64
+ dashboard = null
65
+ } = {}) {
66
+ const summary = diagnostics?.diagnostics ?? { detected: 0, errors: 0 };
67
+ const launchableCount = countLaunchableAgents(dashboard);
68
+ const detected = summary.detected ?? 0;
69
+ const agentsReady = launchableCount;
70
+ const activeRuns = dashboard?.activeRuns?.length ?? 0;
71
+ const intelConfigured = hasIntelligenceConfigured(diagnostics);
72
+ const summaryLine = `${agentsReady} agents ready · ${detected} detected · ${activeRuns} active runs`;
73
+
74
+ if (!hasGlobalState || detected === 0) {
75
+ return {
76
+ kind: READINESS_KINDS.NEEDS_SETUP,
77
+ label: READINESS_LABELS[READINESS_KINDS.NEEDS_SETUP],
78
+ headline: "NEEDS SETUP",
79
+ summaryLine,
80
+ capabilityLines: [
81
+ "Intelligence: Optional capability not configured"
82
+ ],
83
+ healthKind: "warn"
84
+ };
85
+ }
86
+
87
+ if (hasBlockingDiagnostics(diagnostics) || launchableCount === 0) {
88
+ return {
89
+ kind: READINESS_KINDS.NEEDS_ATTENTION,
90
+ label: READINESS_LABELS[READINESS_KINDS.NEEDS_ATTENTION],
91
+ headline: "NEEDS ATTENTION",
92
+ summaryLine,
93
+ capabilityLines: [
94
+ intelConfigured
95
+ ? "Intelligence: Configured"
96
+ : "Intelligence: Optional capability not configured"
97
+ ],
98
+ healthKind: "error"
99
+ };
100
+ }
101
+
102
+ if (!intelConfigured) {
103
+ return {
104
+ kind: READINESS_KINDS.LIMITED,
105
+ label: READINESS_LABELS[READINESS_KINDS.LIMITED],
106
+ headline: "LIMITED",
107
+ summaryLine,
108
+ capabilityLines: [
109
+ "Intelligence: Optional capability not configured"
110
+ ],
111
+ healthKind: "warn"
112
+ };
113
+ }
114
+
115
+ return {
116
+ kind: READINESS_KINDS.READY,
117
+ label: READINESS_LABELS[READINESS_KINDS.READY],
118
+ headline: "READY TO WORK",
119
+ summaryLine,
120
+ capabilityLines: [
121
+ "Intelligence: Configured"
122
+ ],
123
+ healthKind: "ready"
124
+ };
125
+ }
126
+
17
127
  /**
18
128
  * Contextual next step from existing diagnostics + dashboard snapshot.
19
- * Priority: configure → review problemsenable intelligence launch.
129
+ * Priority: configure → review when blocked launch when launchable
130
+ * enable intelligence → review.
131
+ * Launchable agents win over missing optional intelligence.
20
132
  */
21
133
  export function resolveDashboardRecommendation({
22
134
  hasGlobalState = false,
@@ -25,42 +137,59 @@ export function resolveDashboardRecommendation({
25
137
  } = {}) {
26
138
  const summary = diagnostics?.diagnostics ?? { detected: 0, errors: 0 };
27
139
  const intelligence = diagnostics?.intelligence?.summary;
28
- const launchableCount = (dashboard?.providers ?? []).filter((entry) => entry.launchable).length;
29
- const hasErrors = (summary.errors ?? 0) > 0;
30
- const hasProblemRecommendation = (diagnostics?.recommendations ?? []).some((line) =>
31
- /error|fix|drift|not detected|failed|problem/i.test(line)
32
- );
140
+ const launchableCount = countLaunchableAgents(dashboard);
141
+ const blocked = hasBlockingDiagnostics(diagnostics);
33
142
 
34
143
  if (!hasGlobalState || (summary.detected ?? 0) === 0) {
35
144
  return {
36
145
  kind: NEXT_STEP_KINDS.CONFIGURE,
37
- message: `Configure the local environment with ${formatCliCommand("setup")}.`
146
+ title: "Finish local setup",
147
+ message: `Configure the local environment with ${formatCliCommand("setup")}.`,
148
+ detail: "Open System health to review what Kairo detected.",
149
+ targetView: RECOMMENDATION_TARGETS.DIAGNOSTICS,
150
+ targetAction: null
38
151
  };
39
152
  }
40
153
 
41
- if (hasErrors || hasProblemRecommendation) {
154
+ if (blocked && launchableCount === 0) {
42
155
  return {
43
156
  kind: NEXT_STEP_KINDS.REVIEW,
44
- message: "Review diagnostics for problems before launching a run."
157
+ title: "Review system health",
158
+ message: "Review System health for problems before launching a run.",
159
+ detail: "Fix agent or configuration issues, then try again.",
160
+ targetView: RECOMMENDATION_TARGETS.DIAGNOSTICS,
161
+ targetAction: null
45
162
  };
46
163
  }
47
164
 
48
- if (!intelligence?.localAvailable && !intelligence?.cloudAuthenticated) {
165
+ if (launchableCount > 0) {
49
166
  return {
50
- kind: NEXT_STEP_KINDS.ENABLE_INTELLIGENCE,
51
- message: "Enable intelligence: start Ollama or set OPENROUTER_API_KEY, then retry."
167
+ kind: NEXT_STEP_KINDS.LAUNCH,
168
+ title: "Create a new run",
169
+ message: "Create a new run",
170
+ detail: "Delegate a task to Cursor, Codex or Claude.",
171
+ targetView: RECOMMENDATION_TARGETS.LAUNCH,
172
+ targetAction: "launch"
52
173
  };
53
174
  }
54
175
 
55
- if (launchableCount > 0) {
176
+ if (!intelligence?.localAvailable && !intelligence?.cloudAuthenticated) {
56
177
  return {
57
- kind: NEXT_STEP_KINDS.LAUNCH,
58
- message: "Launch a supervised run from the menu or with kairo run."
178
+ kind: NEXT_STEP_KINDS.ENABLE_INTELLIGENCE,
179
+ title: "Enable optional intelligence",
180
+ message: "Enable intelligence: start Ollama or set OPENROUTER_API_KEY, then retry.",
181
+ detail: "Agents can still run without this optional capability.",
182
+ targetView: RECOMMENDATION_TARGETS.DIAGNOSTICS,
183
+ targetAction: null
59
184
  };
60
185
  }
61
186
 
62
187
  return {
63
188
  kind: NEXT_STEP_KINDS.REVIEW,
64
- message: "Review diagnostics for problems before launching a run."
189
+ title: "Review system health",
190
+ message: "Review System health for problems before launching a run.",
191
+ detail: "Open System health to inspect agents and configuration.",
192
+ targetView: RECOMMENDATION_TARGETS.DIAGNOSTICS,
193
+ targetAction: null
65
194
  };
66
195
  }
@@ -47,17 +47,24 @@ export function CockpitTopBar({ model, colorEnabled = true }) {
47
47
 
48
48
  export function CockpitNav({ model, colorEnabled = true }) {
49
49
  return React.createElement(Box, { flexDirection: "column" },
50
- model.items.map((item) =>
51
- React.createElement(Text, {
50
+ model.items.map((item) => {
51
+ const suffix = item.current && !item.selected ? " (open)" : "";
52
+ const color = item.focused
53
+ ? (colorEnabled ? COCKPIT_COLORS.primary : undefined)
54
+ : item.selected
55
+ ? (colorEnabled ? COCKPIT_COLORS.secondary : undefined)
56
+ : item.current
57
+ ? (colorEnabled ? COCKPIT_COLORS.muted : undefined)
58
+ : undefined;
59
+ return React.createElement(Text, {
52
60
  key: item.id,
53
61
  bold: item.focused || item.selected,
54
- color: item.focused
55
- ? (colorEnabled ? COCKPIT_COLORS.primary : undefined)
56
- : item.selected
57
- ? (colorEnabled ? COCKPIT_COLORS.secondary : undefined)
58
- : undefined
59
- }, `${item.marker} ${item.label}`)
60
- )
62
+ color
63
+ }, `${item.marker} ${item.label}${suffix}`);
64
+ }),
65
+ model.explanation && React.createElement(Text, {
66
+ color: COCKPIT_COLORS.muted
67
+ }, model.explanation)
61
68
  );
62
69
  }
63
70
 
@@ -108,6 +108,9 @@ export function reduceCockpitUi(state, action) {
108
108
  ...state,
109
109
  view: nextView,
110
110
  listIndex: 0,
111
+ navIndex: action.navIndex == null
112
+ ? state.navIndex
113
+ : clamp(action.navIndex, 0, COCKPIT_NAV.length - 1),
111
114
  region: action.region ?? defaultRegionForView(nextView, state.layoutMode),
112
115
  returnView: action.returnView ?? state.returnView,
113
116
  helpOpen: nextView === ORCHESTRATOR_VIEWS.HELP
@@ -0,0 +1,115 @@
1
+ import { ORCHESTRATOR_VIEWS } from "./orchestrator-state.js";
2
+ import {
3
+ DASHBOARD_PURPOSE,
4
+ resolveDashboardRecommendation,
5
+ resolveProjectReadiness
6
+ } from "../dashboard-guidance.js";
7
+ import { LAYOUT_MODES } from "./layout.js";
8
+
9
+ export function formatHomeRecentRun(run) {
10
+ if (!run) return null;
11
+ const agent = humanizeId(run.agentId);
12
+ const result = humanizeRunState(run.state);
13
+ return {
14
+ headline: `Last run · ${agent} · ${result}`,
15
+ hint: "Open History to inspect the result."
16
+ };
17
+ }
18
+
19
+ export function buildHomeMissionModel({
20
+ projectName = "project",
21
+ hasGlobalState = false,
22
+ diagnostics = null,
23
+ dashboard = null,
24
+ layoutMode = LAYOUT_MODES.COMPACT,
25
+ recentRuns = null
26
+ } = {}) {
27
+ const readiness = resolveProjectReadiness({ hasGlobalState, diagnostics, dashboard });
28
+ const recommendation = resolveDashboardRecommendation({
29
+ hasGlobalState,
30
+ diagnostics,
31
+ dashboard
32
+ });
33
+ const runs = recentRuns ?? dashboard?.recentRuns ?? [];
34
+ const lastRun = formatHomeRecentRun(runs[0] ?? null);
35
+ const includeEmbeddedStatus = layoutMode !== LAYOUT_MODES.WIDE;
36
+
37
+ return {
38
+ title: `HOME — ${projectName}`,
39
+ purpose: DASHBOARD_PURPOSE,
40
+ readiness,
41
+ includeEmbeddedStatus,
42
+ next: {
43
+ title: "NEXT",
44
+ actionTitle: recommendation.title,
45
+ actionDetail: recommendation.detail ?? recommendation.message,
46
+ enterHint: "Enter →",
47
+ kind: recommendation.kind,
48
+ targetView: recommendation.targetView,
49
+ targetAction: recommendation.targetAction,
50
+ message: recommendation.message
51
+ },
52
+ recent: lastRun
53
+ ? {
54
+ title: "RECENT",
55
+ headline: lastRun.headline,
56
+ hint: lastRun.hint,
57
+ emptyHint: null
58
+ }
59
+ : {
60
+ title: "RECENT",
61
+ headline: null,
62
+ hint: null,
63
+ emptyHint: "No runs yet. Create a new run when an agent is ready."
64
+ },
65
+ explore: {
66
+ title: "EXPLORE",
67
+ lines: [
68
+ "Agents — See which installed tools Kairo can launch and audit.",
69
+ "System health — Inspect intelligence, authentication, and configuration."
70
+ ]
71
+ },
72
+ recommendedTitle: recommendation.title,
73
+ recommendedAction: recommendation.message,
74
+ recommendedKind: recommendation.kind,
75
+ recommendedTargetView: recommendation.targetView,
76
+ recommendedTargetAction: recommendation.targetAction,
77
+ emptyHint: lastRun ? null : "No runs yet. Create a new run when an agent is ready."
78
+ };
79
+ }
80
+
81
+ export function openRecommendedDestination(recommendation, { navItems } = {}) {
82
+ if (!recommendation?.targetView) return null;
83
+ const index = (navItems ?? []).findIndex((item) => item.view === recommendation.targetView
84
+ || (recommendation.targetAction && item.action === recommendation.targetAction));
85
+ return {
86
+ view: recommendation.targetView,
87
+ action: recommendation.targetAction ?? null,
88
+ navIndex: index >= 0 ? index : null
89
+ };
90
+ }
91
+
92
+ function humanizeId(value) {
93
+ const raw = String(value ?? "agent");
94
+ return raw.charAt(0).toUpperCase() + raw.slice(1);
95
+ }
96
+
97
+ function humanizeRunState(state) {
98
+ switch (state) {
99
+ case "succeeded":
100
+ case "completed":
101
+ return "Succeeded";
102
+ case "failed":
103
+ return "Failed";
104
+ case "cancelled":
105
+ case "canceled":
106
+ return "Cancelled";
107
+ case "running":
108
+ case "starting":
109
+ return "Running";
110
+ default:
111
+ return humanizeId(state);
112
+ }
113
+ }
114
+
115
+ export { ORCHESTRATOR_VIEWS };
@@ -1,9 +1,9 @@
1
- import { LAYOUT_MODES } from "./layout.js";
1
+ import { LAYOUT_MODES, resolveListLimit } from "./layout.js";
2
2
  import { ORCHESTRATOR_VIEWS } from "./orchestrator-state.js";
3
- import { resolveDashboardRecommendation } from "../dashboard-guidance.js";
3
+ import { resolveProjectReadiness } from "../dashboard-guidance.js";
4
4
  import { STATUS_LABELS, resolveGlyphs } from "./theme.js";
5
5
  import { windowList } from "./list-window.js";
6
- import { resolveListLimit } from "./layout.js";
6
+ import { buildHomeMissionModel, formatHomeRecentRun } from "./cockpit-home.js";
7
7
 
8
8
  export const COCKPIT_REGIONS = {
9
9
  NAV: "nav",
@@ -12,12 +12,43 @@ export const COCKPIT_REGIONS = {
12
12
  };
13
13
 
14
14
  export const COCKPIT_NAV = [
15
- { id: "overview", label: "Overview", view: ORCHESTRATOR_VIEWS.HOME },
16
- { id: "active", label: "Active runs", view: ORCHESTRATOR_VIEWS.ACTIVE_RUNS },
17
- { id: "recent", label: "Recent runs", view: ORCHESTRATOR_VIEWS.RECENT_RUNS },
18
- { id: "providers", label: "Providers", view: ORCHESTRATOR_VIEWS.PROVIDERS },
19
- { id: "launch", label: "Launch run", view: ORCHESTRATOR_VIEWS.LAUNCH, action: "launch" },
20
- { id: "diagnostics", label: "Diagnostics", view: ORCHESTRATOR_VIEWS.DIAGNOSTICS }
15
+ {
16
+ id: "overview",
17
+ label: "Home",
18
+ view: ORCHESTRATOR_VIEWS.HOME,
19
+ description: "What Kairo does here, readiness, and the next useful action."
20
+ },
21
+ {
22
+ id: "active",
23
+ label: "Running now",
24
+ view: ORCHESTRATOR_VIEWS.ACTIVE_RUNS,
25
+ description: "Supervised runs executing right now."
26
+ },
27
+ {
28
+ id: "recent",
29
+ label: "History",
30
+ view: ORCHESTRATOR_VIEWS.RECENT_RUNS,
31
+ description: "Past runs with agent, state, and readable result."
32
+ },
33
+ {
34
+ id: "providers",
35
+ label: "Agents",
36
+ view: ORCHESTRATOR_VIEWS.PROVIDERS,
37
+ description: "Installed tools Kairo can launch and audit."
38
+ },
39
+ {
40
+ id: "launch",
41
+ label: "New run",
42
+ view: ORCHESTRATOR_VIEWS.LAUNCH,
43
+ action: "launch",
44
+ description: "Delegate a task to an executable agent."
45
+ },
46
+ {
47
+ id: "diagnostics",
48
+ label: "System health",
49
+ view: ORCHESTRATOR_VIEWS.DIAGNOSTICS,
50
+ description: "Agents, intelligence, authentication, and configuration."
51
+ }
21
52
  ];
22
53
 
23
54
  export function regionsForLayout(layoutMode) {
@@ -30,6 +61,11 @@ export function regionsForLayout(layoutMode) {
30
61
  return [COCKPIT_REGIONS.CONTENT];
31
62
  }
32
63
 
64
+ export function navIndexForView(view, items = COCKPIT_NAV) {
65
+ const index = items.findIndex((item) => item.view === view);
66
+ return index >= 0 ? index : 0;
67
+ }
68
+
33
69
  export function buildTopBarModel({
34
70
  projectName = "project",
35
71
  systemOnline = true,
@@ -46,28 +82,73 @@ export function buildTopBarModel({
46
82
  };
47
83
  }
48
84
 
85
+ export function resolveNavStatusSummary(item, { dashboard = null, diagnostics = null } = {}) {
86
+ const active = dashboard?.activeRuns?.length ?? 0;
87
+ const recent = dashboard?.recentRuns?.length ?? 0;
88
+ const providers = dashboard?.providers ?? [];
89
+ const launchable = providers.filter((entry) => entry.launchable).length;
90
+ const detected = diagnostics?.diagnostics?.detected ?? providers.filter((p) => p.available).length;
91
+ const errors = diagnostics?.diagnostics?.errors ?? 0;
92
+
93
+ switch (item.id) {
94
+ case "overview":
95
+ return resolveProjectReadiness({
96
+ hasGlobalState: true,
97
+ diagnostics,
98
+ dashboard
99
+ }).label;
100
+ case "active":
101
+ return active === 0 ? "Idle" : `${active} active`;
102
+ case "recent":
103
+ return recent === 0 ? "No history" : `${recent} recent`;
104
+ case "providers":
105
+ return `${launchable}/${providers.length || detected} ready`;
106
+ case "launch":
107
+ return launchable > 0 ? "Ready" : "Unavailable";
108
+ case "diagnostics":
109
+ return errors > 0 ? `${errors} issues` : "Checked";
110
+ default:
111
+ return "";
112
+ }
113
+ }
114
+
49
115
  export function buildNavModel({
50
116
  navIndex = 0,
117
+ currentView = ORCHESTRATOR_VIEWS.HOME,
51
118
  focused = false,
52
119
  unicode = true,
53
- items = COCKPIT_NAV
120
+ items = COCKPIT_NAV,
121
+ dashboard = null,
122
+ diagnostics = null
54
123
  } = {}) {
55
124
  const glyphs = resolveGlyphs(unicode);
125
+ const selected = items[navIndex] ?? items[0];
126
+ const mapped = items.map((item, index) => {
127
+ const isSelected = index === navIndex;
128
+ const isCurrent = item.view === currentView;
129
+ return {
130
+ ...item,
131
+ marker: isSelected ? glyphs.focus : (isCurrent ? glyphs.bullet : " "),
132
+ selected: isSelected,
133
+ current: isCurrent,
134
+ focused: focused && isSelected,
135
+ statusSummary: resolveNavStatusSummary(item, { dashboard, diagnostics })
136
+ };
137
+ });
138
+
56
139
  return {
57
140
  title: "NAVIGATION",
58
- items: items.map((item, index) => ({
59
- ...item,
60
- marker: index === navIndex ? glyphs.focus : " ",
61
- selected: index === navIndex,
62
- focused: focused && index === navIndex
63
- }))
141
+ explanation: selected
142
+ ? `${selected.description} (${resolveNavStatusSummary(selected, { dashboard, diagnostics })})`
143
+ : "",
144
+ items: mapped
64
145
  };
65
146
  }
66
147
 
67
148
  export function buildSystemStripModel({
68
149
  dashboard = null,
69
150
  diagnostics = null,
70
- healthKind = "ready"
151
+ readiness = null
71
152
  } = {}) {
72
153
  const agentsDetected = diagnostics?.diagnostics?.detected
73
154
  ?? (dashboard?.providers ?? []).filter((p) => p.available).length;
@@ -81,6 +162,11 @@ export function buildSystemStripModel({
81
162
  : intelligence?.cloudAuthenticated
82
163
  ? "Cloud"
83
164
  : "None";
165
+ const resolved = readiness ?? resolveProjectReadiness({
166
+ hasGlobalState: agentsDetected > 0,
167
+ diagnostics,
168
+ dashboard
169
+ });
84
170
 
85
171
  return {
86
172
  title: "SYSTEM",
@@ -88,50 +174,32 @@ export function buildSystemStripModel({
88
174
  { key: "Agents", value: `${agentsDetected}/${agentsTotal}`, kind: agentsDetected > 0 ? "ready" : "warn" },
89
175
  { key: "Runs", value: String(activeRuns), kind: activeRuns > 0 ? "ready" : "muted" },
90
176
  { key: "Intel", value: intelLabel, kind: intelLabel === "None" ? "warn" : "ready" },
91
- { key: "Health", value: STATUS_LABELS[healthKind] ?? healthKind, kind: healthKind }
177
+ {
178
+ key: "Health",
179
+ value: STATUS_LABELS[resolved.kind] ?? resolved.label ?? resolved.kind,
180
+ kind: resolved.healthKind ?? "warn"
181
+ }
92
182
  ]
93
183
  };
94
184
  }
95
185
 
96
- export function buildHomeMissionModel({
97
- hasGlobalState = false,
98
- diagnostics = null,
99
- dashboard = null,
100
- layoutMode = LAYOUT_MODES.COMPACT,
101
- activityLines = []
102
- } = {}) {
103
- const recommendation = resolveDashboardRecommendation({
104
- hasGlobalState,
105
- diagnostics,
106
- dashboard
107
- });
108
- const limit = resolveListLimit(layoutMode, { contentRows: 10 });
109
- const windowed = windowList(activityLines, limit);
110
-
111
- return {
112
- title: "MISSION CONTROL",
113
- recommendedTitle: "Recommended action",
114
- recommendedAction: recommendation.message,
115
- recommendedKind: recommendation.kind,
116
- activityTitle: activityLines.length ? "Activity" : "Activity / empty state",
117
- activityLines: windowed.items,
118
- moreLine: windowed.moreLine,
119
- emptyHint: activityLines.length === 0
120
- ? "No recent activity. Launch a run when agents are ready."
121
- : null
122
- };
123
- }
124
-
125
186
  export function buildFooterModel({
126
187
  view = ORCHESTRATOR_VIEWS.HOME,
127
188
  region = COCKPIT_REGIONS.NAV,
128
189
  helpOpen = false,
129
190
  canCancel = false,
130
- unicode = true
191
+ unicode = true,
192
+ hasError = false
131
193
  } = {}) {
132
194
  const glyphs = resolveGlyphs(unicode);
133
195
  const parts = [];
134
196
 
197
+ if (hasError) {
198
+ parts.push("R Retry");
199
+ parts.push("Esc Exit");
200
+ return { text: parts.join(` ${glyphs.bullet} `) };
201
+ }
202
+
135
203
  if (helpOpen || view === ORCHESTRATOR_VIEWS.HELP) {
136
204
  parts.push("Esc close help");
137
205
  parts.push("? Help");
@@ -173,8 +241,15 @@ export function buildFooterModel({
173
241
  return { text: parts.join(` ${glyphs.bullet} `) };
174
242
  }
175
243
 
244
+ export function windowLinesForLayout(lines = [], layoutMode = LAYOUT_MODES.COMPACT, contentRows = 12) {
245
+ const limit = resolveListLimit(layoutMode, { contentRows });
246
+ return windowList(lines, limit);
247
+ }
248
+
176
249
  export function resolveProjectName(workspaceRoot = "") {
177
250
  if (!workspaceRoot) return "project";
178
251
  const parts = String(workspaceRoot).split(/[/\\]/).filter(Boolean);
179
252
  return parts[parts.length - 1] || "project";
180
253
  }
254
+
255
+ export { buildHomeMissionModel, formatHomeRecentRun };
@@ -3,38 +3,60 @@ import { Box, Text } from "ink";
3
3
  import { COCKPIT_COLORS } from "./theme.js";
4
4
  import { CockpitEmptyState } from "./cockpit/primitives.js";
5
5
  import {
6
- formatDiagnosticsLines,
7
- formatLaunchWizardLines,
8
6
  formatProviderLines,
9
7
  formatRunDetailLines,
10
8
  formatRunLines,
9
+ formatSystemHealthLines,
10
+ formatLaunchWizardLines,
11
11
  ORCHESTRATOR_VIEWS,
12
12
  LAUNCH_WIZARD_STEPS
13
13
  } from "./orchestrator-state.js";
14
+ import { windowLinesForLayout } from "./cockpit-models.js";
15
+ import { LAYOUT_MODES } from "./layout.js";
14
16
 
15
17
  export function HomeMissionPanel({ model, colorEnabled = true }) {
18
+ const readiness = model.readiness;
16
19
  return React.createElement(Box, { flexDirection: "column" },
17
20
  React.createElement(Text, {
18
21
  bold: true,
19
22
  color: colorEnabled ? COCKPIT_COLORS.secondary : undefined
20
23
  }, model.title),
21
- React.createElement(Text, { bold: true }, model.recommendedTitle),
24
+ React.createElement(Text, null, model.purpose),
25
+ React.createElement(Text, null, ""),
26
+ React.createElement(Box, { flexDirection: "column" },
27
+ React.createElement(Text, {
28
+ bold: true,
29
+ color: colorEnabled ? COCKPIT_COLORS.primary : undefined
30
+ }, readiness.headline),
31
+ React.createElement(Text, null, readiness.summaryLine),
32
+ ...(readiness.capabilityLines ?? []).map((line) =>
33
+ React.createElement(Text, { key: line, color: COCKPIT_COLORS.muted }, line)
34
+ ),
35
+ React.createElement(Text, null, "")
36
+ ),
37
+ React.createElement(Text, { bold: true }, model.next.title),
22
38
  React.createElement(Text, {
39
+ bold: true,
23
40
  color: colorEnabled ? COCKPIT_COLORS.primary : undefined
24
- }, model.recommendedAction),
41
+ }, model.next.actionTitle),
42
+ React.createElement(Text, null, model.next.actionDetail),
43
+ React.createElement(Text, { color: COCKPIT_COLORS.muted }, model.next.enterHint),
25
44
  React.createElement(Text, null, ""),
26
- React.createElement(Text, { bold: true }, model.activityTitle),
27
- model.emptyHint
45
+ React.createElement(Text, { bold: true }, model.recent.title),
46
+ model.recent.emptyHint
28
47
  ? React.createElement(CockpitEmptyState, {
29
- message: model.emptyHint,
30
- hint: "Enter on Launch run when ready."
48
+ message: model.recent.emptyHint,
49
+ hint: "Enter opens the recommended next step."
31
50
  })
32
- : model.activityLines.map((line) =>
33
- React.createElement(Text, { key: line }, line)
51
+ : React.createElement(Box, { flexDirection: "column" },
52
+ React.createElement(Text, null, model.recent.headline),
53
+ React.createElement(Text, { color: COCKPIT_COLORS.muted }, model.recent.hint)
34
54
  ),
35
- model.moreLine && React.createElement(Text, {
36
- color: COCKPIT_COLORS.muted
37
- }, model.moreLine)
55
+ React.createElement(Text, null, ""),
56
+ React.createElement(Text, { bold: true }, model.explore.title),
57
+ model.explore.lines.map((line) =>
58
+ React.createElement(Text, { key: line, color: COCKPIT_COLORS.muted }, line)
59
+ )
38
60
  );
39
61
  }
40
62
 
@@ -51,6 +73,7 @@ export function renderCockpitView({
51
73
  selectedRun,
52
74
  selectedEvents,
53
75
  homeMission,
76
+ layoutMode = LAYOUT_MODES.COMPACT,
54
77
  colorEnabled = true
55
78
  }) {
56
79
  switch (view) {
@@ -58,34 +81,52 @@ export function renderCockpitView({
58
81
  return React.createElement(HomeMissionPanel, { model: homeMission, colorEnabled });
59
82
  case ORCHESTRATOR_VIEWS.ACTIVE_RUNS:
60
83
  return listBlock(
61
- "Active runs",
62
- formatRunLines(dashboard?.activeRuns ?? [], { emptyMessage: "No active runs." }),
84
+ "Running now",
85
+ formatRunLines(dashboard?.activeRuns ?? [], {
86
+ emptyMessage: "No runs are executing. Open New run to start one.",
87
+ readable: true
88
+ }),
63
89
  listIndex,
64
- colorEnabled
90
+ colorEnabled,
91
+ "Nothing is running yet — that means Kairo is idle, not broken."
65
92
  );
66
93
  case ORCHESTRATOR_VIEWS.RECENT_RUNS:
67
94
  return listBlock(
68
- "Recent runs",
69
- formatRunLines(dashboard?.recentRuns ?? [], { emptyMessage: "No completed runs yet." }),
95
+ "History",
96
+ formatRunLines(dashboard?.recentRuns ?? [], {
97
+ emptyMessage: "No completed runs yet. Create a new run to build history.",
98
+ readable: true
99
+ }),
70
100
  listIndex,
71
- colorEnabled
101
+ colorEnabled,
102
+ "History fills after supervised runs finish."
72
103
  );
73
- case ORCHESTRATOR_VIEWS.PROVIDERS:
104
+ case ORCHESTRATOR_VIEWS.PROVIDERS: {
105
+ const lines = formatProviderLines(dashboard?.providers ?? []);
106
+ const windowed = windowLinesForLayout(lines, layoutMode);
74
107
  return React.createElement(Box, { flexDirection: "column" },
75
- React.createElement(Text, { bold: true }, "Providers"),
76
- formatProviderLines(dashboard?.providers ?? [])
77
- .map((line) => React.createElement(Text, { key: line }, line))
108
+ React.createElement(Text, { bold: true }, "Agents"),
109
+ lines.length === 0
110
+ ? React.createElement(CockpitEmptyState, {
111
+ message: "No agents detected yet.",
112
+ hint: "Open System health or finish setup, then return here."
113
+ })
114
+ : windowed.items.map((line) => React.createElement(Text, { key: line }, line)),
115
+ windowed.moreLine && React.createElement(Text, {
116
+ color: COCKPIT_COLORS.muted
117
+ }, windowed.moreLine)
78
118
  );
119
+ }
79
120
  case ORCHESTRATOR_VIEWS.LAUNCH:
80
121
  if (launchableAgents.length === 0) {
81
122
  return React.createElement(CockpitEmptyState, {
82
- title: "Launch run",
83
- message: "No launchable agents detected.",
84
- hint: "Esc to return · check Diagnostics"
123
+ title: "New run",
124
+ message: "No executable agents are ready. Kairo cannot start a run yet.",
125
+ hint: "Esc to return · open System health or Agents"
85
126
  });
86
127
  }
87
128
  return React.createElement(Box, { flexDirection: "column" },
88
- React.createElement(Text, { bold: true }, "Launch run"),
129
+ React.createElement(Text, { bold: true }, "New run"),
89
130
  formatLaunchWizardLines({
90
131
  step: launchStep,
91
132
  draft: launchDraft,
@@ -105,18 +146,23 @@ export function renderCockpitView({
105
146
  formatRunDetailLines(selectedRun, selectedEvents)
106
147
  .map((line) => React.createElement(Text, { key: line }, line))
107
148
  );
108
- case ORCHESTRATOR_VIEWS.DIAGNOSTICS:
149
+ case ORCHESTRATOR_VIEWS.DIAGNOSTICS: {
150
+ const lines = formatSystemHealthLines(diagnostics);
151
+ const windowed = windowLinesForLayout(lines, layoutMode);
109
152
  return React.createElement(Box, { flexDirection: "column" },
110
- React.createElement(Text, { bold: true }, "Diagnostics"),
111
- formatDiagnosticsLines(diagnostics)
112
- .map((line) => React.createElement(Text, { key: line }, line))
153
+ React.createElement(Text, { bold: true }, "System health"),
154
+ windowed.items.map((line) => React.createElement(Text, { key: line }, line)),
155
+ windowed.moreLine && React.createElement(Text, {
156
+ color: COCKPIT_COLORS.muted
157
+ }, windowed.moreLine)
113
158
  );
159
+ }
114
160
  case ORCHESTRATOR_VIEWS.HELP:
115
161
  return React.createElement(Box, { flexDirection: "column" },
116
162
  React.createElement(Text, { bold: true }, "Help"),
117
- React.createElement(Text, null, "Kairo runtime launches and audits agent CLIs you manage."),
118
- React.createElement(Text, null, "↑↓ navigate · Tab region · Enter open · Esc back/exit"),
119
- React.createElement(Text, null, "R refresh · C cancel active run · ? toggle help"),
163
+ React.createElement(Text, null, "Kairo coordinates installed AI agents with controlled, auditable runs."),
164
+ React.createElement(Text, null, "↑↓ navigate · Enter open recommended or selected section · Esc back/exit"),
165
+ React.createElement(Text, null, "Tab region (lists / New run) · R refresh/retry · C cancel · ? help"),
120
166
  React.createElement(Text, null, "CLI: kairo run --agent <id> --task \"...\""),
121
167
  React.createElement(Text, null, "Audit trail: ~/.harness/runs/<runId>/")
122
168
  );
@@ -127,14 +173,20 @@ export function renderCockpitView({
127
173
  }
128
174
  }
129
175
 
130
- function listBlock(title, lines, listIndex, colorEnabled) {
176
+ function listBlock(title, lines, listIndex, colorEnabled, emptyHint) {
177
+ const isEmpty = lines.length === 1 && /no |nothing |empty/i.test(lines[0]);
131
178
  return React.createElement(Box, { flexDirection: "column" },
132
179
  React.createElement(Text, { bold: true }, title),
133
- lines.map((line, index) => React.createElement(Text, {
134
- key: `${index}-${line}`,
135
- bold: index === listIndex,
136
- color: index === listIndex && colorEnabled ? COCKPIT_COLORS.primary : undefined
137
- }, `${index === listIndex ? "› " : " "}${line}`))
180
+ isEmpty
181
+ ? React.createElement(CockpitEmptyState, {
182
+ message: lines[0],
183
+ hint: emptyHint
184
+ })
185
+ : lines.map((line, index) => React.createElement(Text, {
186
+ key: `${index}-${line}`,
187
+ bold: index === listIndex,
188
+ color: index === listIndex && colorEnabled ? COCKPIT_COLORS.primary : undefined
189
+ }, `${index === listIndex ? "› " : " "}${line}`))
138
190
  );
139
191
  }
140
192
 
@@ -13,6 +13,7 @@ import {
13
13
  isContentInteractiveView
14
14
  } from "./cockpit-controller.js";
15
15
  import {
16
+ COCKPIT_NAV,
16
17
  COCKPIT_REGIONS,
17
18
  buildFooterModel,
18
19
  buildHomeMissionModel,
@@ -21,6 +22,8 @@ import {
21
22
  buildTopBarModel,
22
23
  resolveProjectName
23
24
  } from "./cockpit-models.js";
25
+ import { openRecommendedDestination } from "./cockpit-home.js";
26
+ import { resolveProjectReadiness } from "../dashboard-guidance.js";
24
27
  import { CockpitShell } from "./cockpit/primitives.js";
25
28
  import { renderCockpitView } from "./cockpit-views.js";
26
29
  import { handleLaunchInput } from "./launch-input.js";
@@ -66,11 +69,40 @@ export function OrchestratorApp({
66
69
  exit();
67
70
  };
68
71
 
72
+ const openDestination = (recommendation) => {
73
+ const destination = openRecommendedDestination(recommendation, { navItems: COCKPIT_NAV });
74
+ if (!destination) return false;
75
+ if (destination.action === "launch") {
76
+ data.resetLaunchWizard();
77
+ }
78
+ dispatch({
79
+ type: "set-view",
80
+ view: destination.view,
81
+ navIndex: destination.navIndex ?? undefined
82
+ });
83
+ return true;
84
+ };
85
+
69
86
  useInput((inputKey, key) => {
70
- if (data.loading || data.error || data.busy) return;
87
+ if (data.loading) return;
88
+
89
+ if (data.error) {
90
+ if (key.escape) {
91
+ finish({ cancelled: true });
92
+ return;
93
+ }
94
+ if (inputKey.toLowerCase() === "r") {
95
+ data.setError(null);
96
+ data.reload().catch((reloadError) => {
97
+ data.setError(reloadError instanceof Error ? reloadError.message : String(reloadError));
98
+ });
99
+ }
100
+ return;
101
+ }
102
+
103
+ if (data.busy) return;
71
104
 
72
105
  if (key.escape) {
73
- // Launch wizard may retreat a step before leaving the view.
74
106
  if (ui.view === ORCHESTRATOR_VIEWS.LAUNCH && data.launchableAgents.length > 0) {
75
107
  const retreated = handleLaunchInput({
76
108
  key,
@@ -130,9 +162,23 @@ export function OrchestratorApp({
130
162
  if (routed) {
131
163
  if (routed.type === "enter-nav") {
132
164
  const item = resolveNavAction(ui.navIndex);
165
+ if (item?.id === "overview" || item?.view === ORCHESTRATOR_VIEWS.HOME) {
166
+ const mission = buildHomeMissionModel({
167
+ projectName: resolveProjectName(workspaceRoot),
168
+ hasGlobalState,
169
+ diagnostics: data.diagnostics,
170
+ dashboard: data.dashboard,
171
+ layoutMode: ui.layoutMode ?? LAYOUT_MODES.COMPACT
172
+ });
173
+ if (openDestination(mission.next)) return;
174
+ }
133
175
  if (item?.action === "launch") {
134
176
  data.resetLaunchWizard();
135
- dispatch({ type: "set-view", view: ORCHESTRATOR_VIEWS.LAUNCH });
177
+ dispatch({
178
+ type: "set-view",
179
+ view: ORCHESTRATOR_VIEWS.LAUNCH,
180
+ navIndex: ui.navIndex
181
+ });
136
182
  return;
137
183
  }
138
184
  }
@@ -140,7 +186,6 @@ export function OrchestratorApp({
140
186
  return;
141
187
  }
142
188
 
143
- // View-specific handlers after centralized region routing.
144
189
  if (ui.view === ORCHESTRATOR_VIEWS.LAUNCH && data.launchableAgents.length > 0) {
145
190
  if (handleLaunchInput({
146
191
  key,
@@ -201,13 +246,26 @@ export function OrchestratorApp({
201
246
  return React.createElement(Box, { flexDirection: "column" },
202
247
  React.createElement(Text, { bold: true, color: COCKPIT_COLORS.danger }, "Runtime error"),
203
248
  React.createElement(Text, null, data.error),
204
- React.createElement(Text, { dimColor: true }, "Esc to exit")
249
+ React.createElement(Text, { dimColor: true }, "R Retry · Esc to exit")
205
250
  );
206
251
  }
207
252
 
208
253
  const mode = ui.layoutMode ?? LAYOUT_MODES.COMPACT;
209
254
  const colorEnabled = caps.color;
210
255
  const unicode = caps.unicode;
256
+ const projectName = resolveProjectName(workspaceRoot);
257
+ const readiness = resolveProjectReadiness({
258
+ hasGlobalState,
259
+ diagnostics: data.diagnostics,
260
+ dashboard: data.dashboard
261
+ });
262
+ const homeMission = buildHomeMissionModel({
263
+ projectName,
264
+ hasGlobalState,
265
+ diagnostics: data.diagnostics,
266
+ dashboard: data.dashboard,
267
+ layoutMode: mode
268
+ });
211
269
 
212
270
  return React.createElement(Box, { flexDirection: "column" },
213
271
  data.statusMessage && React.createElement(Text, {
@@ -215,8 +273,8 @@ export function OrchestratorApp({
215
273
  }, data.statusMessage),
216
274
  React.createElement(CockpitShell, {
217
275
  topBar: buildTopBarModel({
218
- projectName: resolveProjectName(workspaceRoot),
219
- systemOnline: true,
276
+ projectName,
277
+ systemOnline: readiness.kind !== "needs_setup",
220
278
  unicode
221
279
  }),
222
280
  footer: buildFooterModel({
@@ -229,13 +287,16 @@ export function OrchestratorApp({
229
287
  layoutMode: mode,
230
288
  nav: buildNavModel({
231
289
  navIndex: ui.navIndex,
290
+ currentView: ui.view,
232
291
  focused: ui.region === COCKPIT_REGIONS.NAV || !isContentInteractiveView(ui.view),
233
- unicode
292
+ unicode,
293
+ dashboard: data.dashboard,
294
+ diagnostics: data.diagnostics
234
295
  }),
235
296
  system: buildSystemStripModel({
236
297
  dashboard: data.dashboard,
237
298
  diagnostics: data.diagnostics,
238
- healthKind: "ready"
299
+ readiness
239
300
  }),
240
301
  navFocused: ui.region === COCKPIT_REGIONS.NAV,
241
302
  contentFocused: ui.region === COCKPIT_REGIONS.CONTENT,
@@ -254,15 +315,8 @@ export function OrchestratorApp({
254
315
  launchableAgents: data.launchableAgents,
255
316
  selectedRun: data.selectedRun,
256
317
  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
- }),
318
+ homeMission,
319
+ layoutMode: mode,
266
320
  colorEnabled
267
321
  })
268
322
  )
@@ -23,11 +23,11 @@ export const ORCHESTRATOR_VIEWS = {
23
23
  };
24
24
 
25
25
  export const ORCHESTRATOR_MENU = [
26
- { id: "active", label: "Active runs", view: ORCHESTRATOR_VIEWS.ACTIVE_RUNS },
27
- { id: "recent", label: "Recent runs", view: ORCHESTRATOR_VIEWS.RECENT_RUNS },
28
- { id: "providers", label: "Providers", view: ORCHESTRATOR_VIEWS.PROVIDERS },
29
- { id: "launch", label: "Launch run", view: ORCHESTRATOR_VIEWS.LAUNCH, action: "launch" },
30
- { id: "diagnostics", label: "Diagnostics", view: ORCHESTRATOR_VIEWS.DIAGNOSTICS },
26
+ { id: "active", label: "Running now", view: ORCHESTRATOR_VIEWS.ACTIVE_RUNS },
27
+ { id: "recent", label: "History", view: ORCHESTRATOR_VIEWS.RECENT_RUNS },
28
+ { id: "providers", label: "Agents", view: ORCHESTRATOR_VIEWS.PROVIDERS },
29
+ { id: "launch", label: "New run", view: ORCHESTRATOR_VIEWS.LAUNCH, action: "launch" },
30
+ { id: "diagnostics", label: "System health", view: ORCHESTRATOR_VIEWS.DIAGNOSTICS },
31
31
  { id: "help", label: "Help", view: ORCHESTRATOR_VIEWS.HELP }
32
32
  ];
33
33
 
@@ -157,13 +157,22 @@ export function shiftMenuIndex(currentIndex, direction, menuLength = ORCHESTRATO
157
157
  return Math.min(menuLength - 1, Math.max(0, currentIndex + delta));
158
158
  }
159
159
 
160
- export function formatRunLines(runs, { emptyMessage = "No runs." } = {}) {
160
+ export function formatRunLines(runs, {
161
+ emptyMessage = "No runs.",
162
+ readable = false
163
+ } = {}) {
161
164
  if (!runs || runs.length === 0) return [emptyMessage];
162
165
 
163
166
  return runs.map((run) => {
164
- const state = run.state.padEnd(12);
165
- const agent = run.agentId.padEnd(10);
166
- return `${run.runId} ${state} ${agent} ${formatTaskLabel(run)}`;
167
+ const agent = humanizeToken(run.agentId);
168
+ const state = humanizeRunState(run.state);
169
+ const task = formatTaskLabel(run);
170
+ if (readable) {
171
+ return `${agent} · ${state} · ${task}`;
172
+ }
173
+ const statePad = run.state.padEnd(12);
174
+ const agentPad = run.agentId.padEnd(10);
175
+ return `${run.runId} ${statePad} ${agentPad} ${task}`;
167
176
  });
168
177
  }
169
178
 
@@ -181,17 +190,42 @@ export function formatProviderLines(providers) {
181
190
  }
182
191
 
183
192
  export function formatDiagnosticsLines(diagnostics) {
193
+ return formatSystemHealthLines(diagnostics);
194
+ }
195
+
196
+ export function formatSystemHealthLines(diagnostics) {
184
197
  const summary = diagnostics?.diagnostics;
198
+ const intelligence = diagnostics?.intelligence?.summary;
199
+ const profile = diagnostics?.profile;
200
+ const capabilities = diagnostics?.capabilities ?? [];
201
+ const authKnown = capabilities.filter((entry) => entry.authenticated != null);
202
+ const authReady = authKnown.filter((entry) => entry.authenticated).length;
203
+
185
204
  const lines = [
186
- "Summary",
187
- `CLI version: ${diagnostics?.cliVersion ?? "unknown"}`,
188
- `Agents detected: ${summary?.detected ?? 0}/${diagnostics?.capabilities?.length ?? 0}`,
205
+ "Agents",
206
+ `Detected: ${summary?.detected ?? 0}/${capabilities.length}`,
189
207
  `Available: ${summary?.available ?? 0}`,
190
208
  `Unknown: ${summary?.unknown ?? 0}`,
191
209
  `Errors: ${summary?.errors ?? 0}`,
210
+ ...formatAgentStatusLines(capabilities),
211
+ "",
212
+ "Intelligence",
213
+ intelligence?.localAvailable
214
+ ? "Local backend available"
215
+ : "Local backend unavailable",
216
+ intelligence?.cloudAuthenticated
217
+ ? "Cloud backend authenticated"
218
+ : "Cloud backend not configured",
219
+ `Routing: ${diagnostics?.intelligence?.routingPreview?.reason ?? "n/a"}`,
220
+ "",
221
+ "Authentication",
222
+ authKnown.length === 0
223
+ ? "No agent authentication signals yet"
224
+ : `${authReady}/${authKnown.length} agents report ready auth`,
192
225
  "",
193
- "Agent capabilities",
194
- ...formatAgentStatusLines(diagnostics?.capabilities ?? [])
226
+ "Configuration",
227
+ `CLI version: ${diagnostics?.cliVersion ?? "unknown"}`,
228
+ `Profile sources: ${(profile?.sources ?? []).join(", ") || "none"}`
195
229
  ];
196
230
 
197
231
  const recommendations = diagnostics?.recommendations ?? [];
@@ -265,6 +299,29 @@ export function isRunCancellable(run) {
265
299
  return run && isActiveRunState(run.state);
266
300
  }
267
301
 
302
+ function humanizeToken(value) {
303
+ const raw = String(value ?? "agent");
304
+ return raw.charAt(0).toUpperCase() + raw.slice(1);
305
+ }
306
+
307
+ function humanizeRunState(state) {
308
+ switch (state) {
309
+ case "succeeded":
310
+ case "completed":
311
+ return "Succeeded";
312
+ case "failed":
313
+ return "Failed";
314
+ case "cancelled":
315
+ case "canceled":
316
+ return "Cancelled";
317
+ case "running":
318
+ case "starting":
319
+ return "Running";
320
+ default:
321
+ return humanizeToken(state);
322
+ }
323
+ }
324
+
268
325
  function summarizeEvent(event) {
269
326
  if (event.type === "agent.tool_call") {
270
327
  return event.data?.tool_name ?? event.data?.name ?? "tool";
@@ -19,7 +19,10 @@ export const STATUS_LABELS = {
19
19
  offline: "Offline",
20
20
  local: "Local",
21
21
  online: "ONLINE",
22
- loading: "Loading"
22
+ loading: "Loading",
23
+ needs_setup: "Needs setup",
24
+ needs_attention: "Needs attention",
25
+ limited: "Limited"
23
26
  };
24
27
 
25
28
  export const COCKPIT_GLYPHS = {
@@ -55,9 +58,12 @@ export function statusColor(kind, { colorEnabled = true } = {}) {
55
58
  return COCKPIT_COLORS.success;
56
59
  case "warn":
57
60
  case "warning":
61
+ case "needs_setup":
62
+ case "limited":
58
63
  return COCKPIT_COLORS.warning;
59
64
  case "error":
60
65
  case "danger":
66
+ case "needs_attention":
61
67
  return COCKPIT_COLORS.danger;
62
68
  case "offline":
63
69
  case "muted":