@kal-elsam/kairo-runtime 0.4.3 → 0.5.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 (103) hide show
  1. package/README.md +124 -17
  2. package/global-template/components/catalog.json +63 -6
  3. package/global-template/components/sdd-core/personas/teaching.md +42 -0
  4. package/global-template/components/sdd-core/skills/sdd-apply/SKILL.md +34 -0
  5. package/global-template/components/sdd-core/skills/sdd-apply/references/contract.md +4 -0
  6. package/global-template/components/sdd-core/skills/sdd-archive/SKILL.md +34 -0
  7. package/global-template/components/sdd-core/skills/sdd-archive/references/contract.md +4 -0
  8. package/global-template/components/sdd-core/skills/sdd-design/SKILL.md +34 -0
  9. package/global-template/components/sdd-core/skills/sdd-design/references/contract.md +4 -0
  10. package/global-template/components/sdd-core/skills/sdd-explore/SKILL.md +34 -0
  11. package/global-template/components/sdd-core/skills/sdd-explore/references/contract.md +4 -0
  12. package/global-template/components/sdd-core/skills/sdd-init/SKILL.md +34 -0
  13. package/global-template/components/sdd-core/skills/sdd-init/references/contract.md +4 -0
  14. package/global-template/components/sdd-core/skills/sdd-propose/SKILL.md +35 -0
  15. package/global-template/components/sdd-core/skills/sdd-propose/references/contract.md +4 -0
  16. package/global-template/components/sdd-core/skills/sdd-spec/SKILL.md +34 -0
  17. package/global-template/components/sdd-core/skills/sdd-spec/references/contract.md +4 -0
  18. package/global-template/components/sdd-core/skills/sdd-tasks/SKILL.md +34 -0
  19. package/global-template/components/sdd-core/skills/sdd-tasks/references/contract.md +4 -0
  20. package/global-template/components/sdd-core/skills/sdd-verify/SKILL.md +34 -0
  21. package/global-template/components/sdd-core/skills/sdd-verify/references/contract.md +4 -0
  22. package/package.json +1 -1
  23. package/scripts/cockpit-smoke.mjs +29 -8
  24. package/scripts/smoke-test.sh +9 -0
  25. package/src/cli.js +54 -5
  26. package/src/global/action-planner.js +17 -4
  27. package/src/global/component-authoring.js +1 -2
  28. package/src/global/component-ecosystem-checks.js +74 -15
  29. package/src/global/component-health.js +56 -0
  30. package/src/global/component-integration-cli.js +302 -0
  31. package/src/global/component-manifest.js +214 -0
  32. package/src/global/component-registry.js +19 -2
  33. package/src/global/component-resolve.js +51 -0
  34. package/src/global/components/sdd-core.js +6 -0
  35. package/src/global/control-plane-proposals.js +229 -0
  36. package/src/global/control-plane-snapshot.js +212 -0
  37. package/src/global/dashboard-guidance.js +27 -3
  38. package/src/global/diff.js +2 -5
  39. package/src/global/global-cli.js +16 -1
  40. package/src/global/global-doctor.js +75 -2
  41. package/src/global/global-installer.js +43 -6
  42. package/src/global/governance-actions.js +212 -0
  43. package/src/global/governance-repair.js +13 -0
  44. package/src/global/ink/cockpit-changes.js +124 -0
  45. package/src/global/ink/cockpit-control-center.js +106 -0
  46. package/src/global/ink/cockpit-controller.js +17 -0
  47. package/src/global/ink/cockpit-enter.js +27 -0
  48. package/src/global/ink/cockpit-focus.js +7 -1
  49. package/src/global/ink/cockpit-models.js +98 -46
  50. package/src/global/ink/cockpit-proposals.js +82 -0
  51. package/src/global/ink/cockpit-recovery.js +120 -0
  52. package/src/global/ink/cockpit-runs.js +47 -0
  53. package/src/global/ink/cockpit-scan.js +60 -0
  54. package/src/global/ink/cockpit-views.js +144 -74
  55. package/src/global/ink/orchestrator-app.js +156 -42
  56. package/src/global/ink/orchestrator-state.js +6 -0
  57. package/src/global/ink/use-orchestrator-data.js +239 -17
  58. package/src/global/integrations/engram-apply.js +105 -0
  59. package/src/global/integrations/engram-evidence.js +218 -0
  60. package/src/global/integrations/engram-exec.js +107 -0
  61. package/src/global/integrations/engram-plan.js +60 -0
  62. package/src/global/integrations/engram-provider.js +22 -0
  63. package/src/global/integrations/engram-receipts.js +116 -0
  64. package/src/global/integrations/engram-rollback.js +131 -0
  65. package/src/global/integrations/index.js +12 -0
  66. package/src/global/integrations/provider-contract.js +83 -0
  67. package/src/global/integrations/provider-registry.js +47 -0
  68. package/src/global/integrations/sdd-apply.js +190 -0
  69. package/src/global/integrations/sdd-destinations.js +95 -0
  70. package/src/global/integrations/sdd-evidence.js +89 -0
  71. package/src/global/integrations/sdd-fs-guard.js +183 -0
  72. package/src/global/integrations/sdd-lifecycle.js +84 -0
  73. package/src/global/integrations/sdd-persona.js +71 -0
  74. package/src/global/integrations/sdd-plan.js +87 -0
  75. package/src/global/integrations/sdd-provider.js +21 -0
  76. package/src/global/integrations/sdd-receipts.js +44 -0
  77. package/src/global/integrations/sdd-rollback.js +269 -0
  78. package/src/global/integrations/sdd-skill-files.js +61 -0
  79. package/src/global/integrations/sdd-state.js +175 -0
  80. package/src/global/integrations/sdd-verify.js +122 -0
  81. package/src/global/intelligence/backends/opencode-catalog.js +212 -0
  82. package/src/global/intelligence/backends/opencode-cli.js +247 -0
  83. package/src/global/intelligence/backends/opencode-evidence.js +129 -0
  84. package/src/global/intelligence/backends/opencode-http.js +152 -0
  85. package/src/global/intelligence/backends/opencode-invoke.js +83 -0
  86. package/src/global/intelligence/backends/opencode-providers.js +67 -0
  87. package/src/global/intelligence/backends/opencode-runtime.js +242 -0
  88. package/src/global/intelligence/context-compiler.js +95 -50
  89. package/src/global/intelligence/index.js +38 -1
  90. package/src/global/intelligence/orchestrate.js +4 -2
  91. package/src/global/intelligence/registry.js +62 -6
  92. package/src/global/intelligence/router.js +188 -70
  93. package/src/global/intelligence/transport-registry.js +147 -0
  94. package/src/global/intelligence/types.js +21 -2
  95. package/src/global/intelligence-cli.js +48 -17
  96. package/src/global/intelligence-session.js +37 -0
  97. package/src/global/json-output.js +1 -0
  98. package/src/global/load-component-catalog.js +10 -28
  99. package/src/global/load-workspace-component-catalog.js +22 -63
  100. package/src/global/state-migration.js +21 -1
  101. package/src/global/state.js +13 -8
  102. package/src/global/status.js +4 -17
  103. package/src/global/sync.js +2 -5
@@ -13,9 +13,12 @@ import {
13
13
  } from "./orchestrator-state.js";
14
14
  import { windowLinesForLayout } from "./cockpit-models.js";
15
15
  import { LAYOUT_MODES } from "./layout.js";
16
+ import { formatRunsHubLines, RUNS_HUB_ITEMS } from "./cockpit-runs.js";
17
+ import { formatChangesActionLines } from "./cockpit-changes.js";
18
+ import { formatRecoveryLines } from "./cockpit-recovery.js";
16
19
 
17
- export function HomeMissionPanel({ model, colorEnabled = true }) {
18
- const readiness = model.readiness;
20
+ export function ControlCenterPanel({ model, colorEnabled = true }) {
21
+ const health = model.health;
19
22
  return React.createElement(Box, { flexDirection: "column" },
20
23
  React.createElement(Text, {
21
24
  bold: true,
@@ -23,40 +26,36 @@ export function HomeMissionPanel({ model, colorEnabled = true }) {
23
26
  }, model.title),
24
27
  React.createElement(Text, null, model.purpose),
25
28
  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),
38
29
  React.createElement(Text, {
39
30
  bold: true,
40
31
  color: colorEnabled ? COCKPIT_COLORS.primary : undefined
41
- }, model.next.actionTitle),
42
- React.createElement(Text, null, model.next.actionDetail),
43
- React.createElement(Text, { color: COCKPIT_COLORS.muted }, model.next.enterHint),
32
+ }, health.label),
33
+ React.createElement(Text, null, health.summaryLine),
34
+ ...(model.coverageLines ?? []).map((line) =>
35
+ React.createElement(Text, { key: line, color: COCKPIT_COLORS.muted }, line)
36
+ ),
44
37
  React.createElement(Text, null, ""),
45
- React.createElement(Text, { bold: true }, model.recent.title),
46
- model.recent.emptyHint
47
- ? React.createElement(CockpitEmptyState, {
48
- message: model.recent.emptyHint,
49
- hint: "Enter opens the recommended next step."
50
- })
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)
54
- ),
38
+ React.createElement(Text, { bold: true }, model.cta.title),
39
+ React.createElement(Text, {
40
+ bold: true,
41
+ color: colorEnabled ? COCKPIT_COLORS.primary : undefined
42
+ }, model.cta.actionTitle),
43
+ React.createElement(Text, null, model.cta.actionDetail),
44
+ React.createElement(Text, { color: COCKPIT_COLORS.muted }, model.cta.enterHint),
45
+ model.notes?.length > 0 && React.createElement(Box, { flexDirection: "column", marginTop: 1 },
46
+ React.createElement(Text, { bold: true }, "NOTES"),
47
+ model.notes.map((line) =>
48
+ React.createElement(Text, { key: line, color: COCKPIT_COLORS.muted }, line)
49
+ )
50
+ ),
51
+ model.proposalLines?.length > 0 && React.createElement(Box, { flexDirection: "column", marginTop: 1 },
52
+ React.createElement(Text, { bold: true }, "PROPOSALS"),
53
+ model.proposalLines.map((line) =>
54
+ React.createElement(Text, { key: line, color: COCKPIT_COLORS.muted }, line)
55
+ )
56
+ ),
55
57
  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
- )
58
+ React.createElement(Text, { color: COCKPIT_COLORS.muted }, model.runsSecondaryHint)
60
59
  );
61
60
  }
62
61
 
@@ -64,65 +63,85 @@ export function renderCockpitView({
64
63
  view,
65
64
  dashboard,
66
65
  diagnostics,
66
+ snapshot,
67
67
  listIndex,
68
68
  launchStep,
69
69
  launchDraft,
70
70
  launchAgentIndex,
71
71
  launchPermissionIndex,
72
72
  launchableAgents,
73
+ controlCenter,
74
+ layoutMode = LAYOUT_MODES.COMPACT,
73
75
  selectedRun,
74
76
  selectedEvents,
75
- homeMission,
76
- layoutMode = LAYOUT_MODES.COMPACT,
77
+ changesAction = null,
78
+ recoveryAction = null,
77
79
  colorEnabled = true
78
80
  }) {
79
81
  switch (view) {
80
82
  case ORCHESTRATOR_VIEWS.HOME:
81
- return React.createElement(HomeMissionPanel, { model: homeMission, colorEnabled });
83
+ return React.createElement(ControlCenterPanel, { model: controlCenter, colorEnabled });
84
+ case ORCHESTRATOR_VIEWS.IDES:
85
+ case ORCHESTRATOR_VIEWS.PROVIDERS:
86
+ return governanceList("IDEs & models", [
87
+ ...formatProviderLines(dashboard?.providers ?? snapshot?.runtime?.providers ?? []),
88
+ "",
89
+ "Engram / Graphify appear as external integrations when detected; Kairo does not claim to install them."
90
+ ], layoutMode, colorEnabled);
91
+ case ORCHESTRATOR_VIEWS.MODULES:
92
+ return governanceList("Harness modules", formatModuleLines(snapshot), layoutMode, colorEnabled);
93
+ case ORCHESTRATOR_VIEWS.CHANGES:
94
+ return governanceList(
95
+ "Changes",
96
+ formatChangeLines(snapshot, changesAction, layoutMode),
97
+ layoutMode,
98
+ colorEnabled
99
+ );
100
+ case ORCHESTRATOR_VIEWS.ACTIVITY:
101
+ return governanceList(
102
+ "Activity & recovery",
103
+ formatRecoveryLines({ snapshot, recoveryAction, listIndex }),
104
+ layoutMode,
105
+ colorEnabled
106
+ );
107
+ case ORCHESTRATOR_VIEWS.PROFILE:
108
+ return governanceList("Profile & policy", formatProfileLines(snapshot, diagnostics), layoutMode, colorEnabled);
109
+ case ORCHESTRATOR_VIEWS.RUNS:
110
+ return listBlock(
111
+ "Runs",
112
+ formatRunsHubLines(RUNS_HUB_ITEMS),
113
+ listIndex,
114
+ colorEnabled,
115
+ "Choose Active runs, History, or New run."
116
+ );
82
117
  case ORCHESTRATOR_VIEWS.ACTIVE_RUNS:
83
118
  return listBlock(
84
- "Running now",
119
+ "Active runs",
85
120
  formatRunLines(dashboard?.activeRuns ?? [], {
86
- emptyMessage: "No runs are executing. Open New run to start one.",
121
+ emptyMessage: "No runs executing. Governance first launch only after setup/repairs.",
87
122
  readable: true
88
123
  }),
89
124
  listIndex,
90
125
  colorEnabled,
91
- "Nothing is running yet that means Kairo is idle, not broken."
126
+ "Runs are secondary. Prefer Control center Actions when drift or setup remains."
92
127
  );
93
128
  case ORCHESTRATOR_VIEWS.RECENT_RUNS:
94
129
  return listBlock(
95
- "History",
130
+ "Run history",
96
131
  formatRunLines(dashboard?.recentRuns ?? [], {
97
- emptyMessage: "No completed runs yet. Create a new run to build history.",
132
+ emptyMessage: "No completed runs yet.",
98
133
  readable: true
99
134
  }),
100
135
  listIndex,
101
136
  colorEnabled,
102
- "History fills after supervised runs finish."
103
- );
104
- case ORCHESTRATOR_VIEWS.PROVIDERS: {
105
- const lines = formatProviderLines(dashboard?.providers ?? []);
106
- const windowed = windowLinesForLayout(lines, layoutMode);
107
- return React.createElement(Box, { flexDirection: "column" },
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)
137
+ "Open Runs after governance is healthy."
118
138
  );
119
- }
120
139
  case ORCHESTRATOR_VIEWS.LAUNCH:
121
140
  if (launchableAgents.length === 0) {
122
141
  return React.createElement(CockpitEmptyState, {
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"
142
+ title: "Runs",
143
+ message: "No executable agents are ready.",
144
+ hint: "Return to IDEs & models or Changes first."
126
145
  });
127
146
  }
128
147
  return React.createElement(Box, { flexDirection: "column" },
@@ -146,25 +165,20 @@ export function renderCockpitView({
146
165
  formatRunDetailLines(selectedRun, selectedEvents)
147
166
  .map((line) => React.createElement(Text, { key: line }, line))
148
167
  );
149
- case ORCHESTRATOR_VIEWS.DIAGNOSTICS: {
150
- const lines = formatSystemHealthLines(diagnostics);
151
- const windowed = windowLinesForLayout(lines, layoutMode);
152
- return React.createElement(Box, { flexDirection: "column" },
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)
168
+ case ORCHESTRATOR_VIEWS.DIAGNOSTICS:
169
+ return governanceList(
170
+ "System health",
171
+ formatSystemHealthLines(diagnostics),
172
+ layoutMode,
173
+ colorEnabled
158
174
  );
159
- }
160
175
  case ORCHESTRATOR_VIEWS.HELP:
161
176
  return React.createElement(Box, { flexDirection: "column" },
162
177
  React.createElement(Text, { bold: true }, "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"),
166
- React.createElement(Text, null, "CLI: kairo run --agent <id> --task \"...\""),
167
- React.createElement(Text, null, "Audit trail: ~/.harness/runs/<runId>/")
178
+ React.createElement(Text, null, "Kairo keeps IDEs and agents aligned with project architecture and workflows."),
179
+ React.createElement(Text, null, "Primary flow: scan findings preview confirm apply → re-scan."),
180
+ React.createElement(Text, null, "↑↓ navigate · Enter open · Esc back/exit · R refresh/retry · ? help"),
181
+ React.createElement(Text, null, "Runs are secondary after setup and repairs.")
168
182
  );
169
183
  default: {
170
184
  const _exhaustive = view;
@@ -173,6 +187,62 @@ export function renderCockpitView({
173
187
  }
174
188
  }
175
189
 
190
+ function governanceList(title, lines, layoutMode, colorEnabled) {
191
+ const windowed = windowLinesForLayout(lines, layoutMode);
192
+ return React.createElement(Box, { flexDirection: "column" },
193
+ React.createElement(Text, { bold: true }, title),
194
+ lines.length === 0
195
+ ? React.createElement(CockpitEmptyState, {
196
+ message: "No data yet from the read-only scan.",
197
+ hint: "Press R to rescan."
198
+ })
199
+ : windowed.items.map((line) => React.createElement(Text, {
200
+ key: line,
201
+ color: colorEnabled ? undefined : undefined
202
+ }, line)),
203
+ windowed.moreLine && React.createElement(Text, {
204
+ color: COCKPIT_COLORS.muted
205
+ }, windowed.moreLine)
206
+ );
207
+ }
208
+
209
+ function formatModuleLines(snapshot) {
210
+ const components = snapshot?.status?.components ?? [];
211
+ if (components.length === 0) {
212
+ return [
213
+ "No harness modules installed yet.",
214
+ "Orchestrator / SDD-TDD appear after setup.",
215
+ "Engram and Graphify are external integrations Kairo can verify, not install."
216
+ ];
217
+ }
218
+ return [
219
+ ...components.map((entry) => `${entry.id} · ${entry.status} · ${entry.source} · v${entry.version}`),
220
+ "",
221
+ "External integrations are reported only when detectable on this machine."
222
+ ];
223
+ }
224
+
225
+ function formatChangeLines(snapshot, changesAction, layoutMode = LAYOUT_MODES.COMPACT) {
226
+ return formatChangesActionLines({ snapshot, changesAction, layoutMode });
227
+ }
228
+
229
+ function formatProfileLines(snapshot, diagnostics) {
230
+ const policy = snapshot?.policy;
231
+ const sources = diagnostics?.profile?.sources;
232
+ const sourceLabel = sources?.global || sources?.project
233
+ ? [sources.global ? "global" : null, sources.project ? "project" : null].filter(Boolean).join(", ")
234
+ : "none";
235
+ return [
236
+ `Policy profile: ${policy?.profile ?? "none"}`,
237
+ `Apply mode: ${policy?.applyMode ?? "n/a"}`,
238
+ `Preflight: ${policy?.preflight ?? "n/a"}`,
239
+ `Policy source: ${policy?.source ?? "none"}`,
240
+ `Kairo profile sources: ${sourceLabel}`,
241
+ "",
242
+ "Project overrides global overrides defaults. Consent remains explicit for writes."
243
+ ];
244
+ }
245
+
176
246
  function listBlock(title, lines, listIndex, colorEnabled, emptyHint) {
177
247
  const isEmpty = lines.length === 1 && /no |nothing |empty/i.test(lines[0]);
178
248
  return React.createElement(Box, { flexDirection: "column" },
@@ -16,14 +16,17 @@ import {
16
16
  COCKPIT_NAV,
17
17
  COCKPIT_REGIONS,
18
18
  buildFooterModel,
19
- buildHomeMissionModel,
20
19
  buildNavModel,
21
20
  buildSystemStripModel,
22
21
  buildTopBarModel,
22
+ navIndexForView,
23
23
  resolveProjectName
24
24
  } from "./cockpit-models.js";
25
- import { openRecommendedDestination } from "./cockpit-home.js";
25
+ import { buildControlCenterModel } from "./cockpit-control-center.js";
26
+ import { resolveEnterNavIntent } from "./cockpit-enter.js";
27
+ import { resolveRunsHubItem, RUNS_HUB_ITEMS } from "./cockpit-runs.js";
26
28
  import { resolveProjectReadiness } from "../dashboard-guidance.js";
29
+ import { CONTROL_PLANE_HEALTH } from "../control-plane-snapshot.js";
27
30
  import { CockpitShell } from "./cockpit/primitives.js";
28
31
  import { renderCockpitView } from "./cockpit-views.js";
29
32
  import { handleLaunchInput } from "./launch-input.js";
@@ -32,6 +35,8 @@ import { useOrchestratorData } from "./use-orchestrator-data.js";
32
35
  import { resolveTerminalCapabilities } from "./terminal-capabilities.js";
33
36
  import { COCKPIT_COLORS } from "./theme.js";
34
37
  import { LAYOUT_MODES } from "./layout.js";
38
+ import { CHANGES_PHASE } from "./cockpit-changes.js";
39
+ import { RECOVERY_PHASE, listRecoverySnapshots } from "./cockpit-recovery.js";
35
40
 
36
41
  export function OrchestratorApp({
37
42
  homeDir,
@@ -69,16 +74,13 @@ export function OrchestratorApp({
69
74
  exit();
70
75
  };
71
76
 
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
- }
77
+ const openDestination = (destinationKey) => {
78
+ const view = resolveCtaDestinationView(destinationKey);
79
+ if (!view) return false;
78
80
  dispatch({
79
81
  type: "set-view",
80
- view: destination.view,
81
- navIndex: destination.navIndex ?? undefined
82
+ view,
83
+ navIndex: navIndexForView(view)
82
84
  });
83
85
  return true;
84
86
  };
@@ -92,10 +94,9 @@ export function OrchestratorApp({
92
94
  return;
93
95
  }
94
96
  if (inputKey.toLowerCase() === "r") {
95
- data.setError(null);
96
- data.reload().catch((reloadError) => {
97
- data.setError(reloadError instanceof Error ? reloadError.message : String(reloadError));
98
- });
97
+ if (data.retrying) return;
98
+ // Keep the error screen until success so Esc stays available during retry.
99
+ data.reload({ asRetry: true }).catch(() => {});
99
100
  }
100
101
  return;
101
102
  }
@@ -103,6 +104,17 @@ export function OrchestratorApp({
103
104
  if (data.busy) return;
104
105
 
105
106
  if (key.escape) {
107
+ if (ui.view === ORCHESTRATOR_VIEWS.CHANGES
108
+ && data.changesAction?.phase === CHANGES_PHASE.CONFIRMING) {
109
+ data.cancelChanges();
110
+ return;
111
+ }
112
+ if (ui.view === ORCHESTRATOR_VIEWS.ACTIVITY
113
+ && data.recoveryAction?.phase === RECOVERY_PHASE.CONFIRMING) {
114
+ data.cancelRecovery();
115
+ return;
116
+ }
117
+
106
118
  if (ui.view === ORCHESTRATOR_VIEWS.LAUNCH && data.launchableAgents.length > 0) {
107
119
  const retreated = handleLaunchInput({
108
120
  key,
@@ -140,11 +152,15 @@ export function OrchestratorApp({
140
152
  return;
141
153
  }
142
154
 
143
- const listLength = ui.view === ORCHESTRATOR_VIEWS.ACTIVE_RUNS
144
- ? (data.dashboard?.activeRuns ?? []).length
145
- : ui.view === ORCHESTRATOR_VIEWS.RECENT_RUNS
146
- ? (data.dashboard?.recentRuns ?? []).length
147
- : 0;
155
+ const listLength = ui.view === ORCHESTRATOR_VIEWS.RUNS
156
+ ? RUNS_HUB_ITEMS.length
157
+ : ui.view === ORCHESTRATOR_VIEWS.ACTIVE_RUNS
158
+ ? (data.dashboard?.activeRuns ?? []).length
159
+ : ui.view === ORCHESTRATOR_VIEWS.RECENT_RUNS
160
+ ? (data.dashboard?.recentRuns ?? []).length
161
+ : ui.view === ORCHESTRATOR_VIEWS.ACTIVITY
162
+ ? listRecoverySnapshots(data.snapshot).length
163
+ : 0;
148
164
 
149
165
  let routed = null;
150
166
  if (key.tab) {
@@ -162,17 +178,15 @@ export function OrchestratorApp({
162
178
  if (routed) {
163
179
  if (routed.type === "enter-nav") {
164
180
  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;
181
+ const intent = resolveEnterNavIntent({
182
+ currentView: ui.view,
183
+ navItem: item,
184
+ ctaDestination: data.snapshot?.cta?.destination ?? null
185
+ });
186
+ if (intent.kind === "activate-cta") {
187
+ if (openDestination(intent.destination)) return;
174
188
  }
175
- if (item?.action === "launch") {
189
+ if (intent.kind === "launch") {
176
190
  data.resetLaunchWizard();
177
191
  dispatch({
178
192
  type: "set-view",
@@ -207,6 +221,22 @@ export function OrchestratorApp({
207
221
  }
208
222
  }
209
223
 
224
+ if (ui.region === COCKPIT_REGIONS.CONTENT
225
+ && ui.view === ORCHESTRATOR_VIEWS.RUNS
226
+ && key.return) {
227
+ const hubItem = resolveRunsHubItem(ui.listIndex);
228
+ if (!hubItem) return;
229
+ if (hubItem.action === "launch") {
230
+ data.resetLaunchWizard();
231
+ }
232
+ dispatch({
233
+ type: "set-view",
234
+ view: hubItem.view,
235
+ navIndex: navIndexForView(ORCHESTRATOR_VIEWS.RUNS)
236
+ });
237
+ return;
238
+ }
239
+
210
240
  if (ui.region === COCKPIT_REGIONS.CONTENT
211
241
  && (ui.view === ORCHESTRATOR_VIEWS.ACTIVE_RUNS || ui.view === ORCHESTRATOR_VIEWS.RECENT_RUNS)
212
242
  && key.return) {
@@ -228,10 +258,53 @@ export function OrchestratorApp({
228
258
  }
229
259
  }
230
260
 
261
+ if (ui.view === ORCHESTRATOR_VIEWS.CHANGES) {
262
+ const keyName = inputKey.toLowerCase();
263
+ if (keyName === "a") {
264
+ data.previewChanges().then((preview) => {
265
+ if (preview?.setupRequired) openDestination("control-center");
266
+ }).catch(() => {});
267
+ return;
268
+ }
269
+ if (keyName === "y" && data.changesAction?.phase === CHANGES_PHASE.CONFIRMING) {
270
+ data.confirmApplyChanges().then((result) => {
271
+ if (result?.reason === "setup-required") openDestination("control-center");
272
+ }).catch(() => {});
273
+ return;
274
+ }
275
+ if (keyName === "n") {
276
+ if (data.changesAction?.phase === CHANGES_PHASE.CONFIRMING) data.cancelChanges();
277
+ return;
278
+ }
279
+ if (keyName === "r") {
280
+ data.rescanChanges().catch(() => {});
281
+ return;
282
+ }
283
+ }
284
+
285
+ if (ui.view === ORCHESTRATOR_VIEWS.ACTIVITY) {
286
+ const keyName = inputKey.toLowerCase();
287
+ if (key.return && ui.region === COCKPIT_REGIONS.CONTENT) {
288
+ const entry = listRecoverySnapshots(data.snapshot)[ui.listIndex];
289
+ if (entry?.name) data.previewRecovery(entry.name).catch(() => {});
290
+ return;
291
+ }
292
+ if (keyName === "y" && data.recoveryAction?.phase === RECOVERY_PHASE.CONFIRMING) {
293
+ data.confirmApplyRecovery().catch(() => {});
294
+ return;
295
+ }
296
+ if (keyName === "n") {
297
+ if (data.recoveryAction?.phase === RECOVERY_PHASE.CONFIRMING) data.cancelRecovery();
298
+ return;
299
+ }
300
+ if (keyName === "r") {
301
+ data.rescanRecovery().catch(() => {});
302
+ return;
303
+ }
304
+ }
305
+
231
306
  if (inputKey.toLowerCase() === "r" && ui.view !== ORCHESTRATOR_VIEWS.LAUNCH) {
232
- data.reload().catch((reloadError) => {
233
- data.setError(reloadError instanceof Error ? reloadError.message : String(reloadError));
234
- });
307
+ data.reload().catch(() => {});
235
308
  }
236
309
  });
237
310
 
@@ -246,7 +319,8 @@ export function OrchestratorApp({
246
319
  return React.createElement(Box, { flexDirection: "column" },
247
320
  React.createElement(Text, { bold: true, color: COCKPIT_COLORS.danger }, "Runtime error"),
248
321
  React.createElement(Text, null, data.error),
249
- React.createElement(Text, { dimColor: true }, "R Retry · Esc to exit")
322
+ React.createElement(Text, { dimColor: true },
323
+ data.retrying ? "Retrying read-only scan…" : "R Retry · Esc to exit")
250
324
  );
251
325
  }
252
326
 
@@ -259,13 +333,15 @@ export function OrchestratorApp({
259
333
  diagnostics: data.diagnostics,
260
334
  dashboard: data.dashboard
261
335
  });
262
- const homeMission = buildHomeMissionModel({
336
+ const controlCenter = buildControlCenterModel({
263
337
  projectName,
264
- hasGlobalState,
265
- diagnostics: data.diagnostics,
266
- dashboard: data.dashboard,
338
+ snapshot: data.snapshot,
267
339
  layoutMode: mode
268
340
  });
341
+ const systemOnline = data.snapshot
342
+ ? data.snapshot.health !== CONTROL_PLANE_HEALTH.NOT_CONFIGURED
343
+ && data.snapshot.health !== CONTROL_PLANE_HEALTH.CHECK_FAILED
344
+ : readiness.kind !== "needs_setup";
269
345
 
270
346
  return React.createElement(Box, { flexDirection: "column" },
271
347
  data.statusMessage && React.createElement(Text, {
@@ -274,15 +350,18 @@ export function OrchestratorApp({
274
350
  React.createElement(CockpitShell, {
275
351
  topBar: buildTopBarModel({
276
352
  projectName,
277
- systemOnline: readiness.kind !== "needs_setup",
353
+ systemOnline,
278
354
  unicode
279
355
  }),
280
356
  footer: buildFooterModel({
281
357
  view: ui.view,
282
358
  region: ui.region,
359
+ navIndex: ui.navIndex,
283
360
  helpOpen: ui.helpOpen,
284
361
  canCancel: isRunCancellable(data.selectedRun),
285
- unicode
362
+ unicode,
363
+ changesPhase: data.changesAction?.phase ?? null,
364
+ recoveryPhase: data.recoveryAction?.phase ?? null
286
365
  }),
287
366
  layoutMode: mode,
288
367
  nav: buildNavModel({
@@ -291,12 +370,23 @@ export function OrchestratorApp({
291
370
  focused: ui.region === COCKPIT_REGIONS.NAV || !isContentInteractiveView(ui.view),
292
371
  unicode,
293
372
  dashboard: data.dashboard,
294
- diagnostics: data.diagnostics
373
+ diagnostics: data.diagnostics,
374
+ snapshot: data.snapshot
295
375
  }),
296
376
  system: buildSystemStripModel({
297
377
  dashboard: data.dashboard,
298
378
  diagnostics: data.diagnostics,
299
- readiness
379
+ readiness: data.snapshot
380
+ ? {
381
+ kind: data.snapshot.health.toLowerCase(),
382
+ label: controlCenter.health.label,
383
+ healthKind: data.snapshot.health === CONTROL_PLANE_HEALTH.HEALTHY
384
+ ? "ready"
385
+ : data.snapshot.health === CONTROL_PLANE_HEALTH.CHECK_FAILED
386
+ ? "error"
387
+ : "warn"
388
+ }
389
+ : readiness
300
390
  }),
301
391
  navFocused: ui.region === COCKPIT_REGIONS.NAV,
302
392
  contentFocused: ui.region === COCKPIT_REGIONS.CONTENT,
@@ -307,6 +397,7 @@ export function OrchestratorApp({
307
397
  view: ui.view,
308
398
  dashboard: data.dashboard,
309
399
  diagnostics: data.diagnostics,
400
+ snapshot: data.snapshot,
310
401
  listIndex: ui.listIndex,
311
402
  launchStep: data.launchStep,
312
403
  launchDraft: data.launchDraft,
@@ -315,10 +406,33 @@ export function OrchestratorApp({
315
406
  launchableAgents: data.launchableAgents,
316
407
  selectedRun: data.selectedRun,
317
408
  selectedEvents: data.selectedEvents,
318
- homeMission,
409
+ changesAction: data.changesAction,
410
+ recoveryAction: data.recoveryAction,
411
+ controlCenter,
319
412
  layoutMode: mode,
320
413
  colorEnabled
321
414
  })
322
415
  )
323
416
  );
324
417
  }
418
+
419
+ function resolveCtaDestinationView(destinationKey) {
420
+ switch (destinationKey) {
421
+ case "changes":
422
+ return ORCHESTRATOR_VIEWS.CHANGES;
423
+ case "control-center":
424
+ return ORCHESTRATOR_VIEWS.HOME;
425
+ case "ides":
426
+ return ORCHESTRATOR_VIEWS.IDES;
427
+ case "modules":
428
+ return ORCHESTRATOR_VIEWS.MODULES;
429
+ case "activity":
430
+ return ORCHESTRATOR_VIEWS.ACTIVITY;
431
+ case "profile":
432
+ return ORCHESTRATOR_VIEWS.PROFILE;
433
+ case "runs":
434
+ return ORCHESTRATOR_VIEWS.RUNS;
435
+ default:
436
+ return null;
437
+ }
438
+ }
@@ -13,6 +13,12 @@ export function canUseOrchestratorShell({
13
13
 
14
14
  export const ORCHESTRATOR_VIEWS = {
15
15
  HOME: "home",
16
+ IDES: "ides",
17
+ MODULES: "modules",
18
+ CHANGES: "changes",
19
+ ACTIVITY: "activity",
20
+ PROFILE: "profile",
21
+ RUNS: "runs",
16
22
  ACTIVE_RUNS: "active-runs",
17
23
  RECENT_RUNS: "recent-runs",
18
24
  RUN_DETAIL: "run-detail",