@kal-elsam/kairo-runtime 0.8.0 → 0.10.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.
Files changed (49) hide show
  1. package/README.md +41 -15
  2. package/package.json +1 -1
  3. package/scripts/cockpit-smoke.mjs +6 -6
  4. package/scripts/ux-prototype-tty.mjs +9 -0
  5. package/src/cli.js +24 -1
  6. package/src/global/control-plane-proposals.js +2 -1
  7. package/src/global/control-plane-snapshot.js +1 -1
  8. package/src/global/global-doctor.js +2 -0
  9. package/src/global/ink/cockpit/primitives.js +127 -50
  10. package/src/global/ink/cockpit-alerts.js +36 -0
  11. package/src/global/ink/cockpit-changes.js +61 -37
  12. package/src/global/ink/cockpit-control-center.js +79 -53
  13. package/src/global/ink/cockpit-controller.js +98 -15
  14. package/src/global/ink/cockpit-enter.js +1 -0
  15. package/src/global/ink/cockpit-focus.js +4 -2
  16. package/src/global/ink/cockpit-models.js +100 -51
  17. package/src/global/ink/cockpit-palette.js +109 -0
  18. package/src/global/ink/cockpit-path-label.js +19 -0
  19. package/src/global/ink/cockpit-recovery.js +84 -18
  20. package/src/global/ink/cockpit-reviews.js +14 -10
  21. package/src/global/ink/cockpit-runs.js +13 -4
  22. package/src/global/ink/cockpit-settings.js +194 -0
  23. package/src/global/ink/cockpit-usage.js +111 -0
  24. package/src/global/ink/cockpit-views.js +119 -116
  25. package/src/global/ink/orchestrator-app.js +169 -46
  26. package/src/global/ink/orchestrator-state.js +24 -14
  27. package/src/global/ink/setup-app.js +55 -72
  28. package/src/global/ink/setup-state.js +16 -0
  29. package/src/global/ink/theme.js +27 -0
  30. package/src/global/ink/use-orchestrator-data.js +58 -0
  31. package/src/global/ink/ux/live-activity.js +194 -0
  32. package/src/global/ink/ux/live-alerts.js +159 -0
  33. package/src/global/ink/ux/live-governance.js +195 -0
  34. package/src/global/ink/ux/live-orchestration.js +188 -0
  35. package/src/global/ink/ux/live-overview.js +125 -0
  36. package/src/global/ink/ux/live-settings.js +189 -0
  37. package/src/global/ink/ux/live-setup.js +160 -0
  38. package/src/global/ink/ux/live-usage.js +51 -0
  39. package/src/global/ink/ux/semantic.js +84 -0
  40. package/src/global/ink/ux/task-flow-app.js +85 -0
  41. package/src/global/ink/ux/task-flow.js +173 -0
  42. package/src/global/orchestrator.js +37 -20
  43. package/src/global/paths.js +3 -0
  44. package/src/global/runtime/alerts/alert-store.js +216 -0
  45. package/src/global/runtime/alerts/alert-types.js +59 -0
  46. package/src/global/runtime/alerts/alert-validate.js +117 -0
  47. package/src/global/runtime/monitor/monitor-cli.js +62 -0
  48. package/src/global/runtime/monitor/monitor-platform.js +95 -0
  49. package/src/global/runtime/monitor/monitor.js +249 -0
@@ -0,0 +1,194 @@
1
+ /**
2
+ * Curated Settings catalog — browse → preview → confirm (no filesystem install).
3
+ */
4
+
5
+ export const SETTINGS_PHASE = Object.freeze({
6
+ BROWSE: "browse",
7
+ PREVIEW: "preview",
8
+ CONFIRMING: "confirming",
9
+ COMPLETED: "completed"
10
+ });
11
+
12
+ export const CURATED_INTEGRATIONS = Object.freeze([
13
+ Object.freeze({
14
+ id: "pi-usage-widget",
15
+ name: "Pi usage widget",
16
+ version: "0.2.1",
17
+ license: "MIT",
18
+ status: "available",
19
+ capabilities: Object.freeze(["usage-display", "local-only"]),
20
+ permissions: Object.freeze(["full-local-access-on-install"]),
21
+ audit: "pending-security-review",
22
+ summary: "Local Pi package for usage visibility in the agent UI.",
23
+ notes: "Explicit install only. Never auto-applied by Kairo."
24
+ })
25
+ ]);
26
+
27
+ export function createSettingsActionState() {
28
+ return {
29
+ phase: SETTINGS_PHASE.BROWSE,
30
+ selectedId: null,
31
+ message: null,
32
+ receipt: null
33
+ };
34
+ }
35
+
36
+ export function listCuratedIntegrations() {
37
+ return [...CURATED_INTEGRATIONS];
38
+ }
39
+
40
+ export function getCuratedIntegration(id) {
41
+ return CURATED_INTEGRATIONS.find((entry) => entry.id === id) ?? null;
42
+ }
43
+
44
+ export function reduceSettingsAction(state, action) {
45
+ switch (action.type) {
46
+ case "reset":
47
+ return createSettingsActionState();
48
+ case "preview": {
49
+ const entry = getCuratedIntegration(action.id);
50
+ if (!entry) {
51
+ return { ...state, phase: SETTINGS_PHASE.BROWSE, message: "Integration not found." };
52
+ }
53
+ return {
54
+ phase: SETTINGS_PHASE.PREVIEW,
55
+ selectedId: entry.id,
56
+ message: "Enter Confirm · Esc back — no files written yet.",
57
+ receipt: null
58
+ };
59
+ }
60
+ case "confirm-prompt":
61
+ if (state.phase !== SETTINGS_PHASE.PREVIEW || !state.selectedId) return state;
62
+ return {
63
+ ...state,
64
+ phase: SETTINGS_PHASE.CONFIRMING,
65
+ message: "Confirm explicit install intent? Y confirm · N/Esc cancel"
66
+ };
67
+ case "confirm": {
68
+ if (state.phase !== SETTINGS_PHASE.CONFIRMING || !state.selectedId) return state;
69
+ const entry = getCuratedIntegration(state.selectedId);
70
+ return {
71
+ phase: SETTINGS_PHASE.COMPLETED,
72
+ selectedId: state.selectedId,
73
+ message: "Confirmed — no auto-install. Apply via documented Pi/CLI path only.",
74
+ receipt: {
75
+ id: state.selectedId,
76
+ version: entry?.version ?? null,
77
+ confirmedAt: new Date().toISOString(),
78
+ wroteFiles: false
79
+ }
80
+ };
81
+ }
82
+ case "cancel":
83
+ return {
84
+ ...createSettingsActionState(),
85
+ message: "Cancelled — no files written."
86
+ };
87
+ default:
88
+ return state;
89
+ }
90
+ }
91
+
92
+ export function formatSettingsBrowseLines(
93
+ integrations = listCuratedIntegrations(),
94
+ listIndex = 0
95
+ ) {
96
+ if (integrations.length === 0) return ["No curated integrations available."];
97
+ return [
98
+ "CURATED INTEGRATIONS",
99
+ ...integrations.map((entry, index) => {
100
+ const mark = index === listIndex ? "›" : " ";
101
+ return `${mark} ${entry.status} · ${entry.name} · ${entry.version} · ${entry.license}`;
102
+ }),
103
+ "",
104
+ "POLICY",
105
+ "Browse → preview → confirm. Install stays explicit; never automatic.",
106
+ "Enter preview · Esc back"
107
+ ];
108
+ }
109
+
110
+ export function formatSettingsDetailLines(entry, settingsAction = null) {
111
+ if (!entry) return ["Integration not found."];
112
+ const phase = settingsAction?.phase ?? SETTINGS_PHASE.BROWSE;
113
+
114
+ // Completed: receipt first so compact TTY (80×24) shows wroteFiles above the fold.
115
+ if (phase === SETTINGS_PHASE.COMPLETED && settingsAction?.receipt) {
116
+ const receipt = settingsAction.receipt;
117
+ return [
118
+ "RESULT",
119
+ settingsAction.message ?? "Confirmed — no auto-install.",
120
+ "RECEIPT",
121
+ `Id · ${receipt.id} · wroteFiles · ${receipt.wroteFiles}`,
122
+ `Confirmed · ${receipt.confirmedAt}`,
123
+ `${entry.name} · ${entry.version} · ${entry.license}`,
124
+ "Esc back · install stays explicit"
125
+ ];
126
+ }
127
+
128
+ const lines = [
129
+ "INTEGRATION",
130
+ `${entry.name} · ${entry.version}`,
131
+ `Status · ${entry.status} · License · ${entry.license}`,
132
+ `Audit · ${entry.audit}`,
133
+ "",
134
+ "CAPABILITIES",
135
+ entry.capabilities.join(" · ") || "none",
136
+ "",
137
+ "PERMISSIONS",
138
+ entry.permissions.join(" · ") || "none",
139
+ "",
140
+ "SUMMARY",
141
+ entry.summary,
142
+ entry.notes
143
+ ];
144
+ if (phase === SETTINGS_PHASE.PREVIEW || phase === SETTINGS_PHASE.CONFIRMING) {
145
+ lines.push("", "PREVIEW", "No filesystem changes. Confirm only records explicit intent.");
146
+ }
147
+ if (settingsAction?.message) lines.push("", settingsAction.message);
148
+ return lines;
149
+ }
150
+
151
+ export function formatSettingsLines({
152
+ listIndex = 0,
153
+ settingsAction = null,
154
+ snapshot = null,
155
+ diagnostics = null
156
+ } = {}) {
157
+ const integrations = listCuratedIntegrations();
158
+ const phase = settingsAction?.phase ?? SETTINGS_PHASE.BROWSE;
159
+ if (phase !== SETTINGS_PHASE.BROWSE && settingsAction?.selectedId) {
160
+ return formatSettingsDetailLines(
161
+ getCuratedIntegration(settingsAction.selectedId),
162
+ settingsAction
163
+ );
164
+ }
165
+
166
+ const policy = snapshot?.policy;
167
+ const sources = diagnostics?.profile?.sources;
168
+ const sourceLabel = sources?.global || sources?.project
169
+ ? [sources.global ? "global" : null, sources.project ? "project" : null].filter(Boolean).join(", ")
170
+ : "none";
171
+ return [
172
+ ...formatSettingsBrowseLines(integrations, listIndex),
173
+ "",
174
+ "PROFILE & POLICY",
175
+ `Policy · ${policy?.profile ?? "none"} · apply ${policy?.applyMode ?? "n/a"}`,
176
+ `Preflight · ${policy?.preflight ?? "n/a"} · sources · ${sourceLabel}`,
177
+ "",
178
+ `Selected · ${integrations[listIndex]?.id ?? "none"}`
179
+ ];
180
+ }
181
+
182
+ export function buildSettingsFooterParts(phase = SETTINGS_PHASE.BROWSE) {
183
+ switch (phase) {
184
+ case SETTINGS_PHASE.PREVIEW:
185
+ return ["Enter Confirm", "Esc Back"];
186
+ case SETTINGS_PHASE.CONFIRMING:
187
+ return ["Y Confirm", "N/Esc Cancel"];
188
+ case SETTINGS_PHASE.COMPLETED:
189
+ return ["Esc Back", "/ Actions"];
190
+ case SETTINGS_PHASE.BROWSE:
191
+ default:
192
+ return ["↑↓ Select", "Enter Preview", "Esc Nav", "/ Actions"];
193
+ }
194
+ }
@@ -0,0 +1,111 @@
1
+ /**
2
+ * Pure Usage / Tokens model — no React/Ink.
3
+ * Shared by formatUsageLines and SemanticUsagePanel.
4
+ */
5
+ import { LAYOUT_MODES } from "./layout.js";
6
+
7
+ export function usageRunLimit(layoutMode = LAYOUT_MODES.COMPACT) {
8
+ return layoutMode === LAYOUT_MODES.WIDE ? 8 : 3;
9
+ }
10
+
11
+ export function formatMeasuredBudgets(budgets) {
12
+ if (!budgets || typeof budgets !== "object") return null;
13
+ const parts = [];
14
+ if (Number.isFinite(budgets.stableUsedTokens) && Number.isFinite(budgets.stableBudgetTokens)) {
15
+ parts.push(`stable ${budgets.stableUsedTokens}/${budgets.stableBudgetTokens}`);
16
+ }
17
+ if (Number.isFinite(budgets.requestUsedTokens) && Number.isFinite(budgets.requestBudgetTokens)) {
18
+ parts.push(`request ${budgets.requestUsedTokens}/${budgets.requestBudgetTokens}`);
19
+ }
20
+ return parts.length > 0 ? parts.join(" · ") : null;
21
+ }
22
+
23
+ export function resolveConfiguredLimits(dashboard) {
24
+ const resolved = dashboard?.profile?.profile ?? {};
25
+ const configured = [];
26
+ if (Number.isFinite(resolved.tokenBudget)) configured.push(`token ${resolved.tokenBudget}`);
27
+ if (Number.isFinite(resolved.stableContextBudget)) configured.push(`stable ${resolved.stableContextBudget}`);
28
+ if (Number.isFinite(resolved.requestContextBudget)) configured.push(`request ${resolved.requestContextBudget}`);
29
+ return configured;
30
+ }
31
+
32
+ export function hasFiniteUsage(usage) {
33
+ if (!usage || typeof usage !== "object") return false;
34
+ return Number.isFinite(usage.total)
35
+ || Number.isFinite(usage.input)
36
+ || Number.isFinite(usage.output);
37
+ }
38
+
39
+ export function formatRunUsageLabel(run) {
40
+ const usage = run?.tokenUsage;
41
+ if (!hasFiniteUsage(usage)) return null;
42
+ const parts = [];
43
+ if (Number.isFinite(usage.input)) parts.push(`in ${usage.input}`);
44
+ if (Number.isFinite(usage.output)) parts.push(`out ${usage.output}`);
45
+ if (Number.isFinite(usage.total)) parts.push(`total ${usage.total}`);
46
+ return `${run.agentId ?? "agent"} · ${parts.join(" · ")}`;
47
+ }
48
+
49
+ function collectAuditableRuns(dashboard) {
50
+ return [...(dashboard?.activeRuns ?? []), ...(dashboard?.recentRuns ?? [])]
51
+ .filter((run) => hasFiniteUsage(run?.tokenUsage));
52
+ }
53
+
54
+ /** Pure adapter: Callout=status · Measured section owns the value. */
55
+ export function adaptUsageModel({
56
+ snapshot = null,
57
+ dashboard = null,
58
+ layoutMode = LAYOUT_MODES.COMPACT
59
+ } = {}) {
60
+ const measured = formatMeasuredBudgets(snapshot?.budgets);
61
+ const configured = resolveConfiguredLimits(dashboard);
62
+ const allRuns = collectAuditableRuns(dashboard);
63
+ const limit = usageRunLimit(layoutMode);
64
+ const visible = allRuns.slice(0, limit);
65
+ const hidden = Math.max(0, allRuns.length - visible.length);
66
+ const hasEvidence = Boolean(measured) || configured.length > 0 || allRuns.length > 0;
67
+
68
+ return {
69
+ title: "Usage",
70
+ callout: hasEvidence
71
+ ? {
72
+ tone: "info",
73
+ title: "Usage evidence available",
74
+ body: "Budgets and run tokenUsage only — no invented totals."
75
+ }
76
+ : {
77
+ tone: "info",
78
+ title: "Data unavailable",
79
+ body: "No measured budgets, profile limits, or auditable run tokenUsage."
80
+ },
81
+ measured: measured ?? "Data unavailable",
82
+ configured: configured.length > 0
83
+ ? configured.join(" · ")
84
+ : "No profile token budgets configured.",
85
+ runs: visible.map((run, i) => ({
86
+ id: `usage-run-${i}`,
87
+ label: formatRunUsageLabel(run)
88
+ })),
89
+ runTotal: allRuns.length,
90
+ runLimit: limit,
91
+ moreLine: hidden > 0 ? `… ${hidden} more` : null,
92
+ hasEvidence,
93
+ footnote: "Auditable budgets only — no invented token savings."
94
+ };
95
+ }
96
+
97
+ export function formatUsageLinesFromModel(model) {
98
+ const lines = [
99
+ "MEASURED", model.measured, "",
100
+ "CONFIGURED LIMITS", model.configured, "",
101
+ "RUN USAGE"
102
+ ];
103
+ if (model.runs.length === 0) {
104
+ lines.push("No auditable run tokenUsage yet.");
105
+ } else {
106
+ for (const item of model.runs) lines.push(item.label);
107
+ if (model.moreLine) lines.push(model.moreLine);
108
+ }
109
+ lines.push("", model.footnote);
110
+ return lines;
111
+ }
@@ -5,7 +5,6 @@ import { CockpitEmptyState } from "./cockpit/primitives.js";
5
5
  import {
6
6
  formatProviderLines,
7
7
  formatRunDetailLines,
8
- formatRunLines,
9
8
  formatSystemHealthLines,
10
9
  formatLaunchWizardLines,
11
10
  ORCHESTRATOR_VIEWS,
@@ -13,50 +12,63 @@ import {
13
12
  } from "./orchestrator-state.js";
14
13
  import { windowLinesForLayout } from "./cockpit-models.js";
15
14
  import { LAYOUT_MODES } from "./layout.js";
16
- import { formatRunsHubLines, RUNS_HUB_ITEMS } from "./cockpit-runs.js";
17
- import { formatReviewDetailLines, formatReviewListLines } from "./cockpit-reviews.js";
18
- import { formatChangesActionLines } from "./cockpit-changes.js";
19
- import { formatRecoveryLines } from "./cockpit-recovery.js";
15
+ import { SemanticOverviewPanel } from "./ux/live-overview.js";
16
+ import { SemanticGovernancePanel } from "./ux/live-governance.js";
17
+ import { SemanticActivityPanel } from "./ux/live-activity.js";
18
+ import { SemanticOrchestrationPanel } from "./ux/live-orchestration.js";
19
+ import { SemanticAlertsPanel } from "./ux/live-alerts.js";
20
+ import { SemanticSettingsPanel } from "./ux/live-settings.js";
21
+ import { SemanticUsagePanel } from "./ux/live-usage.js";
22
+ import { formatReviewDetailLines } from "./cockpit-reviews.js";
23
+ import { listCuratedIntegrations } from "./cockpit-settings.js";
24
+
25
+ export function PalettePanel({ model, colorEnabled = true }) {
26
+ return React.createElement(Box, { flexDirection: "column" },
27
+ React.createElement(Text, {
28
+ bold: true,
29
+ color: colorEnabled ? COCKPIT_COLORS.secondary : undefined
30
+ }, model.title),
31
+ React.createElement(Text, { color: COCKPIT_COLORS.muted }, model.hint),
32
+ React.createElement(Text, null, ""),
33
+ ...(model.items ?? []).map((item) => React.createElement(Text, {
34
+ key: item.id,
35
+ bold: item.selected,
36
+ color: item.selected && colorEnabled ? COCKPIT_COLORS.primary : undefined
37
+ }, `${item.marker} ${item.label}`))
38
+ );
39
+ }
20
40
 
21
41
  export function ControlCenterPanel({ model, colorEnabled = true }) {
22
- const health = model.health;
42
+ const status = model.status ?? model.health;
43
+ const next = model.nextAction ?? model.cta;
23
44
  return React.createElement(Box, { flexDirection: "column" },
24
45
  React.createElement(Text, {
25
46
  bold: true,
26
47
  color: colorEnabled ? COCKPIT_COLORS.secondary : undefined
27
48
  }, model.title),
28
- React.createElement(Text, null, model.purpose),
29
49
  React.createElement(Text, null, ""),
30
50
  React.createElement(Text, {
31
51
  bold: true,
32
52
  color: colorEnabled ? COCKPIT_COLORS.primary : undefined
33
- }, health.label),
34
- React.createElement(Text, null, health.summaryLine),
35
- ...(model.coverageLines ?? []).map((line) =>
36
- React.createElement(Text, { key: line, color: COCKPIT_COLORS.muted }, line)
37
- ),
53
+ }, status?.label),
54
+ React.createElement(Text, null, status?.summaryLine),
38
55
  React.createElement(Text, null, ""),
39
- React.createElement(Text, { bold: true }, model.cta.title),
56
+ React.createElement(Text, { bold: true }, next?.title ?? "NEXT"),
40
57
  React.createElement(Text, {
41
58
  bold: true,
42
59
  color: colorEnabled ? COCKPIT_COLORS.primary : undefined
43
- }, model.cta.actionTitle),
44
- React.createElement(Text, null, model.cta.actionDetail),
45
- React.createElement(Text, { color: COCKPIT_COLORS.muted }, model.cta.enterHint),
46
- model.notes?.length > 0 && React.createElement(Box, { flexDirection: "column", marginTop: 1 },
47
- React.createElement(Text, { bold: true }, "NOTES"),
48
- model.notes.map((line) =>
49
- React.createElement(Text, { key: line, color: COCKPIT_COLORS.muted }, line)
50
- )
51
- ),
52
- model.proposalLines?.length > 0 && React.createElement(Box, { flexDirection: "column", marginTop: 1 },
53
- React.createElement(Text, { bold: true }, "PROPOSALS"),
54
- model.proposalLines.map((line) =>
55
- React.createElement(Text, { key: line, color: COCKPIT_COLORS.muted }, line)
56
- )
57
- ),
60
+ }, next?.actionTitle),
61
+ next?.actionDetail ? React.createElement(Text, null, next.actionDetail) : null,
62
+ React.createElement(Text, { color: COCKPIT_COLORS.muted }, next?.enterHint),
63
+ React.createElement(Text, null, ""),
64
+ React.createElement(Text, { bold: true }, "ACTIVITY"),
65
+ React.createElement(Text, null, model.activity?.headline ?? "Idle"),
66
+ React.createElement(Text, null, ""),
67
+ React.createElement(Text, { bold: true }, "ALERTS"),
68
+ React.createElement(Text, null, model.alerts?.headline ?? "Alert data unavailable"),
58
69
  React.createElement(Text, null, ""),
59
- React.createElement(Text, { color: COCKPIT_COLORS.muted }, model.runsSecondaryHint)
70
+ React.createElement(Text, { bold: true }, "TOKENS"),
71
+ React.createElement(Text, null, model.tokens?.headline ?? "Data unavailable")
60
72
  );
61
73
  }
62
74
 
@@ -72,18 +84,43 @@ export function renderCockpitView({
72
84
  launchPermissionIndex,
73
85
  launchableAgents,
74
86
  controlCenter,
87
+ palette = null,
75
88
  layoutMode = LAYOUT_MODES.COMPACT,
76
89
  selectedRun,
77
90
  selectedEvents,
78
91
  reviews = [],
79
92
  selectedReview = null,
93
+ alerts = [],
80
94
  changesAction = null,
81
95
  recoveryAction = null,
82
- colorEnabled = true
96
+ settingsAction = null,
97
+ colorEnabled = true,
98
+ unicode = true,
99
+ overviewDetailsOpen = false,
100
+ governanceDetailsOpen = false,
101
+ activityDetailsOpen = false,
102
+ contentFocused = false,
103
+ homeDir = null
83
104
  }) {
105
+ if (palette) {
106
+ return React.createElement(PalettePanel, { model: palette, colorEnabled });
107
+ }
84
108
  switch (view) {
85
109
  case ORCHESTRATOR_VIEWS.HOME:
86
- return React.createElement(ControlCenterPanel, { model: controlCenter, colorEnabled });
110
+ return React.createElement(SemanticOverviewPanel, {
111
+ model: controlCenter,
112
+ detailsOpen: overviewDetailsOpen,
113
+ colorEnabled,
114
+ unicode
115
+ });
116
+ case ORCHESTRATOR_VIEWS.USAGE:
117
+ return React.createElement(SemanticUsagePanel, {
118
+ snapshot,
119
+ dashboard,
120
+ layoutMode,
121
+ colorEnabled,
122
+ unicode
123
+ });
87
124
  case ORCHESTRATOR_VIEWS.IDES:
88
125
  case ORCHESTRATOR_VIEWS.PROVIDERS:
89
126
  return governanceList("IDEs & models", [
@@ -94,59 +131,54 @@ export function renderCockpitView({
94
131
  case ORCHESTRATOR_VIEWS.MODULES:
95
132
  return governanceList("Harness modules", formatModuleLines(snapshot), layoutMode, colorEnabled);
96
133
  case ORCHESTRATOR_VIEWS.CHANGES:
97
- return governanceList(
98
- "Changes",
99
- formatChangeLines(snapshot, changesAction, layoutMode),
134
+ return React.createElement(SemanticGovernancePanel, {
135
+ snapshot,
136
+ changesAction,
137
+ homeDir,
138
+ detailsOpen: governanceDetailsOpen,
100
139
  layoutMode,
101
- colorEnabled
102
- );
140
+ colorEnabled,
141
+ unicode
142
+ });
103
143
  case ORCHESTRATOR_VIEWS.ACTIVITY:
104
- return governanceList(
105
- "Activity & recovery",
106
- formatRecoveryLines({ snapshot, recoveryAction, listIndex }),
144
+ return React.createElement(SemanticActivityPanel, {
145
+ snapshot,
146
+ recoveryAction,
147
+ dashboard,
148
+ listIndex,
149
+ homeDir,
150
+ detailsOpen: activityDetailsOpen,
107
151
  layoutMode,
108
- colorEnabled
109
- );
152
+ contentFocused,
153
+ colorEnabled,
154
+ unicode
155
+ });
110
156
  case ORCHESTRATOR_VIEWS.PROFILE:
111
- return governanceList("Profile & policy", formatProfileLines(snapshot, diagnostics), layoutMode, colorEnabled);
112
- case ORCHESTRATOR_VIEWS.RUNS:
113
- return listBlock(
114
- "Runs",
115
- formatRunsHubLines(RUNS_HUB_ITEMS),
157
+ return React.createElement(SemanticSettingsPanel, {
158
+ integrations: listCuratedIntegrations(),
116
159
  listIndex,
160
+ settingsAction,
161
+ snapshot,
162
+ diagnostics,
163
+ layoutMode,
164
+ contentFocused,
117
165
  colorEnabled,
118
- "Choose Active runs, History, Reviews, or New run."
119
- );
166
+ unicode
167
+ });
168
+ case ORCHESTRATOR_VIEWS.RUNS:
120
169
  case ORCHESTRATOR_VIEWS.ACTIVE_RUNS:
121
- return listBlock(
122
- "Active runs",
123
- formatRunLines(dashboard?.activeRuns ?? [], {
124
- emptyMessage: "No runs executing. Governance first — launch only after setup/repairs.",
125
- readable: true
126
- }),
127
- listIndex,
128
- colorEnabled,
129
- "Runs are secondary. Prefer Control center Actions when drift or setup remains."
130
- );
131
170
  case ORCHESTRATOR_VIEWS.RECENT_RUNS:
132
- return listBlock(
133
- "Run history",
134
- formatRunLines(dashboard?.recentRuns ?? [], {
135
- emptyMessage: "No completed runs yet.",
136
- readable: true
137
- }),
138
- listIndex,
139
- colorEnabled,
140
- "Open Runs after governance is healthy."
141
- );
142
171
  case ORCHESTRATOR_VIEWS.REVIEWS:
143
- return listBlock(
144
- "Reviews",
145
- formatReviewListLines(reviews),
172
+ return React.createElement(SemanticOrchestrationPanel, {
173
+ view,
174
+ dashboard,
175
+ reviews,
146
176
  listIndex,
177
+ layoutMode,
178
+ contentFocused,
147
179
  colorEnabled,
148
- "Receipts are read-only. Launch reviews via kairo review --agent codex|pi."
149
- );
180
+ unicode
181
+ });
150
182
  case ORCHESTRATOR_VIEWS.LAUNCH:
151
183
  if (launchableAgents.length === 0) {
152
184
  return React.createElement(CockpitEmptyState, {
@@ -173,7 +205,7 @@ export function renderCockpitView({
173
205
  case ORCHESTRATOR_VIEWS.RUN_DETAIL:
174
206
  return React.createElement(Box, { flexDirection: "column" },
175
207
  React.createElement(Text, { bold: true }, "Run detail"),
176
- formatRunDetailLines(selectedRun, selectedEvents)
208
+ formatRunDetailLines(selectedRun, selectedEvents, { homeDir })
177
209
  .map((line) => React.createElement(Text, { key: line }, line))
178
210
  );
179
211
  case ORCHESTRATOR_VIEWS.REVIEW_DETAIL:
@@ -182,6 +214,15 @@ export function renderCockpitView({
182
214
  formatReviewDetailLines(selectedReview)
183
215
  .map((line) => React.createElement(Text, { key: line }, line))
184
216
  );
217
+ case ORCHESTRATOR_VIEWS.ALERTS:
218
+ return React.createElement(SemanticAlertsPanel, {
219
+ alerts,
220
+ listIndex,
221
+ layoutMode,
222
+ contentFocused,
223
+ colorEnabled,
224
+ unicode
225
+ });
185
226
  case ORCHESTRATOR_VIEWS.DIAGNOSTICS:
186
227
  return governanceList(
187
228
  "System health",
@@ -194,8 +235,8 @@ export function renderCockpitView({
194
235
  React.createElement(Text, { bold: true }, "Help"),
195
236
  React.createElement(Text, null, "Kairo keeps IDEs and agents aligned with project architecture and workflows."),
196
237
  React.createElement(Text, null, "Primary flow: scan → findings → preview → confirm → apply → re-scan."),
197
- React.createElement(Text, null, "↑↓ navigate · Enter open · Esc back/exit · R refresh/retry · ? help"),
198
- React.createElement(Text, null, "Runs are secondary after setup and repairs. Reviews are read-only receipts.")
238
+ React.createElement(Text, null, "↑↓ navigate · Enter open/activate · / actions · Esc back · R refresh · ? help"),
239
+ React.createElement(Text, null, "Overview hides raw diagnostics Enter opens detail destinations.")
199
240
  );
200
241
  default: {
201
242
  const _exhaustive = view;
@@ -213,8 +254,8 @@ function governanceList(title, lines, layoutMode, colorEnabled) {
213
254
  message: "No data yet from the read-only scan.",
214
255
  hint: "Press R to rescan."
215
256
  })
216
- : windowed.items.map((line) => React.createElement(Text, {
217
- key: line,
257
+ : windowed.items.map((line, index) => React.createElement(Text, {
258
+ key: `${index}-${line}`,
218
259
  color: colorEnabled ? undefined : undefined
219
260
  }, line)),
220
261
  windowed.moreLine && React.createElement(Text, {
@@ -239,42 +280,4 @@ function formatModuleLines(snapshot) {
239
280
  ];
240
281
  }
241
282
 
242
- function formatChangeLines(snapshot, changesAction, layoutMode = LAYOUT_MODES.COMPACT) {
243
- return formatChangesActionLines({ snapshot, changesAction, layoutMode });
244
- }
245
-
246
- function formatProfileLines(snapshot, diagnostics) {
247
- const policy = snapshot?.policy;
248
- const sources = diagnostics?.profile?.sources;
249
- const sourceLabel = sources?.global || sources?.project
250
- ? [sources.global ? "global" : null, sources.project ? "project" : null].filter(Boolean).join(", ")
251
- : "none";
252
- return [
253
- `Policy profile: ${policy?.profile ?? "none"}`,
254
- `Apply mode: ${policy?.applyMode ?? "n/a"}`,
255
- `Preflight: ${policy?.preflight ?? "n/a"}`,
256
- `Policy source: ${policy?.source ?? "none"}`,
257
- `Kairo profile sources: ${sourceLabel}`,
258
- "",
259
- "Project overrides global overrides defaults. Consent remains explicit for writes."
260
- ];
261
- }
262
-
263
- function listBlock(title, lines, listIndex, colorEnabled, emptyHint) {
264
- const isEmpty = lines.length === 1 && /no |nothing |empty/i.test(lines[0]);
265
- return React.createElement(Box, { flexDirection: "column" },
266
- React.createElement(Text, { bold: true }, title),
267
- isEmpty
268
- ? React.createElement(CockpitEmptyState, {
269
- message: lines[0],
270
- hint: emptyHint
271
- })
272
- : lines.map((line, index) => React.createElement(Text, {
273
- key: `${index}-${line}`,
274
- bold: index === listIndex,
275
- color: index === listIndex && colorEnabled ? COCKPIT_COLORS.primary : undefined
276
- }, `${index === listIndex ? "› " : " "}${line}`))
277
- );
278
- }
279
-
280
283
  export { LAUNCH_WIZARD_STEPS };