@kal-elsam/kairo-runtime 0.9.0 → 0.11.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 (33) hide show
  1. package/README.md +14 -8
  2. package/package.json +1 -1
  3. package/scripts/cockpit-smoke.mjs +3 -3
  4. package/scripts/ux-prototype-tty.mjs +9 -0
  5. package/src/global/control-plane-proposals.js +2 -1
  6. package/src/global/control-plane-snapshot.js +1 -1
  7. package/src/global/ink/brand/wordmark.js +50 -0
  8. package/src/global/ink/cockpit/primitives.js +72 -50
  9. package/src/global/ink/cockpit-changes.js +2 -2
  10. package/src/global/ink/cockpit-control-center.js +12 -64
  11. package/src/global/ink/cockpit-controller.js +41 -5
  12. package/src/global/ink/cockpit-enter.js +1 -0
  13. package/src/global/ink/cockpit-models.js +14 -7
  14. package/src/global/ink/cockpit-palette.js +18 -7
  15. package/src/global/ink/cockpit-recovery.js +9 -6
  16. package/src/global/ink/cockpit-usage.js +111 -0
  17. package/src/global/ink/cockpit-views.js +70 -97
  18. package/src/global/ink/orchestrator-app.js +44 -4
  19. package/src/global/ink/setup-app.js +55 -72
  20. package/src/global/ink/setup-state.js +16 -0
  21. package/src/global/ink/theme.js +40 -8
  22. package/src/global/ink/ux/live-activity.js +191 -0
  23. package/src/global/ink/ux/live-alerts.js +156 -0
  24. package/src/global/ink/ux/live-governance.js +191 -0
  25. package/src/global/ink/ux/live-orchestration.js +185 -0
  26. package/src/global/ink/ux/live-overview.js +175 -0
  27. package/src/global/ink/ux/live-settings.js +186 -0
  28. package/src/global/ink/ux/live-setup.js +160 -0
  29. package/src/global/ink/ux/live-usage.js +49 -0
  30. package/src/global/ink/ux/semantic.js +101 -0
  31. package/src/global/ink/ux/task-flow-app.js +85 -0
  32. package/src/global/ink/ux/task-flow.js +173 -0
  33. package/src/global/orchestrator.js +37 -20
package/README.md CHANGED
@@ -16,8 +16,11 @@ as a technical alias).
16
16
  Kairo Runtime and prints a migration warning. Prefer `@kal-elsam/kairo-runtime` and the
17
17
  `kairo` CLI for new installs. See [`packages/harness-bridge/README.md`](packages/harness-bridge/README.md).
18
18
 
19
- Terminal UX aims for Pi-like clarity (clear modes, non-interactive flags, extensible
20
- commands) without depending on Pi as a runtime or adding a Pi adapter.
19
+ Terminal UX is **terminal-first**: bare `kairo` opens the Ink Control Center (or Setup
20
+ when unconfigured). Explicit commands (`setup`, `status`, `sync`, `doctor`, `monitor`,
21
+ `shell`, …) keep their current behavior. Progressive disclosure keeps paths and IDs in
22
+ Details, list caps honest (`… N more`), and Callout reserved for status — never for
23
+ primary values. No web loopback / `kairo ui` in this release.
21
24
 
22
25
  - **npm:** https://www.npmjs.com/package/@kal-elsam/kairo-runtime
23
26
  - **repo:** https://github.com/Kal-elSam/harness
@@ -32,13 +35,11 @@ npx @kal-elsam/kairo-runtime
32
35
  kairo
33
36
  ```
34
37
 
35
- **First run** (no `~/.harness/state.json`): interactive onboardingsafe diagnosis
36
- setup with confirmation → full-screen operations cockpit.
38
+ **First run** (no `~/.harness/state.json`): semantic Setup (Detect Agents
39
+ Components PreviewConfirm) → full-screen Control Center.
37
40
 
38
- **Later runs** (state present): full-screen Control Center oriented around user
39
- tasks overall health, one next action, activity, pending alerts, and guided
40
- governance (scan → evidence → preview → confirm → apply → re-scan → recovery).
41
- Runs stay secondary. Layout adapts to terminal size:
41
+ **Later runs** (state present): semantic Cockpit Overview, Governance, Activity,
42
+ Orchestration, Usage, Settings plus Alerts inbox. Layout adapts to terminal size:
42
43
 
43
44
  | Mode | Size | Layout |
44
45
  |------|------|--------|
@@ -73,6 +74,11 @@ Settings browses a curated catalog (pinned `pi-usage-widget@0.2.1`, MIT).
73
74
  Preview → confirm shows an in-session confirmation receipt (`wroteFiles: false`);
74
75
  it does not persist or install anything.
75
76
 
77
+ ### Usage evidence
78
+
79
+ Usage shows measured budget pairs, profile limits, and finite run `tokenUsage`
80
+ fields only. It never invents totals, costs, or savings.
81
+
76
82
  Respects `NO_COLOR`, `HARNESS_ASCII=1`, and `HARNESS_INK=0`. Status is always labeled
77
83
  in text, never color alone.
78
84
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kal-elsam/kairo-runtime",
3
- "version": "0.9.0",
3
+ "version": "0.11.0",
4
4
  "description": "Kairo Runtime — local agent operating system for Codex, Cursor, Claude, Pi, Engram, and Graphify.",
5
5
  "type": "module",
6
6
  "homepage": "https://github.com/Kal-elSam/harness#readme",
@@ -26,7 +26,7 @@ const root = join(dirname(fileURLToPath(import.meta.url)), "..");
26
26
  const require = createRequire(import.meta.url);
27
27
  const pkg = require(join(root, "package.json"));
28
28
 
29
- assert.equal(pkg.version, "0.9.0");
29
+ assert.equal(pkg.version, "0.11.0");
30
30
  assert.ok(pkg.dependencies["ansi-escapes"]);
31
31
 
32
32
  assert.equal(resolveLayoutMode({ columns: 120, rows: 40 }), LAYOUT_MODES.WIDE);
@@ -132,7 +132,7 @@ const proposalLines = formatProposalLines([
132
132
  id: "setup-local",
133
133
  severity: "high",
134
134
  title: "Finish local setup",
135
- destination: "changes",
135
+ destination: "setup",
136
136
  evidence: [{ type: "status", source: "status.overall", ref: "missing" }]
137
137
  }
138
138
  ], {
@@ -143,7 +143,7 @@ const proposalLines = formatProposalLines([
143
143
  requestBudgetTokens: 10
144
144
  }
145
145
  });
146
- assert.match(proposalLines.join("\n"), /\[HIGH\] Finish local setup → changes/);
146
+ assert.match(proposalLines.join("\n"), /\[HIGH\] Finish local setup → setup/);
147
147
  assert.match(proposalLines.join("\n"), /Budget · stable 1\/10/);
148
148
  assert.match(proposalLines.join("\n"), /evidence: status\.overall/);
149
149
 
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env node
2
+ /** Manual TTY gate: Home → Setup → Overview. No writes. Esc exits. */
3
+ import React from "react";
4
+ import { render } from "ink";
5
+ import { TaskFlowApp } from "../src/global/ink/ux/task-flow-app.js";
6
+
7
+ const app = render(React.createElement(TaskFlowApp, {
8
+ onExit: () => app.unmount()
9
+ }));
@@ -12,6 +12,7 @@ export const PROPOSAL_SEVERITY = Object.freeze({
12
12
  });
13
13
 
14
14
  export const PROPOSAL_DESTINATION = Object.freeze({
15
+ SETUP: "setup",
15
16
  CHANGES: "changes",
16
17
  CONTROL_CENTER: "control-center",
17
18
  IDES: "ides",
@@ -48,7 +49,7 @@ export function buildControlPlaneProposals({
48
49
  PROPOSAL_SEVERITY.HIGH,
49
50
  "Finish local setup",
50
51
  status?.nextAction ?? "Configure the local ecosystem before applying repairs.",
51
- PROPOSAL_DESTINATION.CHANGES,
52
+ PROPOSAL_DESTINATION.SETUP,
52
53
  [
53
54
  ev("status", "status.overall", status?.overall ?? "missing"),
54
55
  ev("health", "control-plane.health", health ?? "NOT_CONFIGURED")
@@ -66,7 +66,7 @@ export function resolveControlPlaneCta({ health, status, diff } = {}) {
66
66
  title: "Finish local setup",
67
67
  detail: status?.nextAction
68
68
  ?? `Run ${formatCliCommand("setup")} to configure the local ecosystem.`,
69
- destination: "changes"
69
+ destination: "setup"
70
70
  };
71
71
  case CONTROL_PLANE_HEALTH.ACTION_REQUIRED:
72
72
  return {
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Static ASCII wordmark for Kairo Overview — no figlet dependency.
3
+ * Hero art appears only on Overview (wide / compact); minimal stays textual.
4
+ */
5
+
6
+ import { LAYOUT_MODES } from "../layout.js";
7
+
8
+ const WORDMARK_WIDE = [
9
+ "╦╔═╔═╗╦╦═╗╔═╗",
10
+ "╠╩╗╠═╣║╠╦╝║ ║",
11
+ "╩ ╩╩ ╩╩╩╚═╚═╝"
12
+ ];
13
+
14
+ const WORDMARK_COMPACT = [
15
+ "╦╔═╔═╗╦╦═╗╔═╗",
16
+ "╩ ╩╩ ╩╩╩╚═╚═╝"
17
+ ];
18
+
19
+ const WORDMARK_WIDE_ASCII = [
20
+ "K A I R O",
21
+ "========="
22
+ ];
23
+
24
+ const WORDMARK_COMPACT_ASCII = [
25
+ "KAIRO"
26
+ ];
27
+
28
+ export function shouldShowWordmark(layoutMode) {
29
+ return layoutMode === LAYOUT_MODES.WIDE || layoutMode === LAYOUT_MODES.COMPACT;
30
+ }
31
+
32
+ /**
33
+ * @param {"wide"|"compact"|"minimal"|string} layoutMode
34
+ * @param {{ unicode?: boolean }} [opts]
35
+ * @returns {string[]}
36
+ */
37
+ export function wordmarkLines(layoutMode, { unicode = true } = {}) {
38
+ if (layoutMode === LAYOUT_MODES.WIDE) {
39
+ return unicode ? WORDMARK_WIDE.slice() : WORDMARK_WIDE_ASCII.slice();
40
+ }
41
+ if (layoutMode === LAYOUT_MODES.COMPACT) {
42
+ return unicode ? WORDMARK_COMPACT.slice() : WORDMARK_COMPACT_ASCII.slice();
43
+ }
44
+ return [];
45
+ }
46
+
47
+ export function overviewBrandTitle(layoutMode) {
48
+ if (shouldShowWordmark(layoutMode)) return null;
49
+ return "KAIRO · Overview";
50
+ }
@@ -1,6 +1,6 @@
1
1
  import React from "react";
2
2
  import { Box, Text } from "ink";
3
- import { COCKPIT_COLORS, statusColor } from "../theme.js";
3
+ import { COCKPIT_COLORS, resolveInkColor, statusColor } from "../theme.js";
4
4
 
5
5
  export function CockpitBadge({ label, kind = "ready", colorEnabled = true }) {
6
6
  return React.createElement(Text, {
@@ -8,36 +8,44 @@ export function CockpitBadge({ label, kind = "ready", colorEnabled = true }) {
8
8
  }, label);
9
9
  }
10
10
 
11
- export function CockpitEmptyState({ title, message, hint }) {
11
+ export function CockpitEmptyState({ title, message, hint, colorEnabled = true }) {
12
12
  return React.createElement(Box, { flexDirection: "column", marginY: 1 },
13
- title && React.createElement(Text, { bold: true, color: COCKPIT_COLORS.secondary }, title),
13
+ title && React.createElement(Text, {
14
+ bold: true,
15
+ color: resolveInkColor(colorEnabled, COCKPIT_COLORS.brand)
16
+ }, title),
14
17
  message && React.createElement(Text, null, message),
15
- hint && React.createElement(Text, { color: COCKPIT_COLORS.muted }, hint)
18
+ hint && React.createElement(Text, {
19
+ color: resolveInkColor(colorEnabled, COCKPIT_COLORS.muted)
20
+ }, hint)
16
21
  );
17
22
  }
18
23
 
19
- export function CockpitPanel({ title, focused = false, width, children }) {
24
+ /** Content region no border; focus is conveyed by interactive children. */
25
+ export function CockpitPanel({ title, focused = false, width, children, colorEnabled = true }) {
20
26
  return React.createElement(Box, {
21
27
  flexDirection: "column",
22
28
  width,
23
- borderStyle: "single",
24
- borderColor: focused ? COCKPIT_COLORS.primary : COCKPIT_COLORS.muted,
25
- paddingX: 1,
29
+ paddingX: 0,
30
+ marginY: 1,
26
31
  flexGrow: 1
27
32
  },
28
33
  title && React.createElement(Text, {
29
34
  bold: true,
30
- color: focused ? COCKPIT_COLORS.primary : COCKPIT_COLORS.secondary
35
+ color: resolveInkColor(
36
+ colorEnabled,
37
+ focused ? COCKPIT_COLORS.interactive : COCKPIT_COLORS.brand
38
+ )
31
39
  }, title),
32
40
  children
33
41
  );
34
42
  }
35
43
 
36
- export function CockpitSection({ title, children }) {
44
+ export function CockpitSection({ title, children, colorEnabled = true }) {
37
45
  return React.createElement(Box, { flexDirection: "column", marginBottom: 1 },
38
46
  title && React.createElement(Text, {
39
47
  bold: true,
40
- color: COCKPIT_COLORS.secondary
48
+ color: resolveInkColor(colorEnabled, COCKPIT_COLORS.brand)
41
49
  }, title),
42
50
  children
43
51
  );
@@ -45,19 +53,33 @@ export function CockpitSection({ title, children }) {
45
53
 
46
54
  export function CockpitKeyHint({ keys, label, colorEnabled = true }) {
47
55
  return React.createElement(Text, {
48
- color: colorEnabled ? COCKPIT_COLORS.muted : undefined
56
+ color: resolveInkColor(colorEnabled, COCKPIT_COLORS.muted)
49
57
  }, `${keys} ${label}`);
50
58
  }
51
59
 
60
+ /** Compact header: brand · status · project — no box-drawing. */
52
61
  export function CockpitTopBar({ model, colorEnabled = true }) {
62
+ const statusKind = String(model.status ?? "").toUpperCase() === "ONLINE"
63
+ ? "online"
64
+ : "muted";
53
65
  return React.createElement(Box, { justifyContent: "space-between", width: "100%" },
66
+ React.createElement(Box, null,
67
+ React.createElement(Text, {
68
+ bold: true,
69
+ color: resolveInkColor(colorEnabled, COCKPIT_COLORS.brand)
70
+ }, model.brand),
71
+ React.createElement(Text, {
72
+ color: resolveInkColor(colorEnabled, COCKPIT_COLORS.muted)
73
+ }, " · "),
74
+ React.createElement(CockpitBadge, {
75
+ label: model.status,
76
+ kind: statusKind,
77
+ colorEnabled
78
+ })
79
+ ),
54
80
  React.createElement(Text, {
55
- bold: true,
56
- color: colorEnabled ? COCKPIT_COLORS.primary : undefined
57
- }, `╭─ ${model.brand} ─ ${model.status}`),
58
- React.createElement(Text, {
59
- color: colorEnabled ? COCKPIT_COLORS.muted : undefined
60
- }, `${model.projectLabel} ─╮`)
81
+ color: resolveInkColor(colorEnabled, COCKPIT_COLORS.muted)
82
+ }, model.projectLabel)
61
83
  );
62
84
  }
63
85
 
@@ -67,11 +89,11 @@ export function CockpitNav({ model, colorEnabled = true }) {
67
89
  model.items.map((item) => {
68
90
  const suffix = item.current && !item.selected ? " (open)" : "";
69
91
  const color = item.focused
70
- ? (colorEnabled ? COCKPIT_COLORS.primary : undefined)
92
+ ? resolveInkColor(colorEnabled, COCKPIT_COLORS.interactive)
71
93
  : item.selected
72
- ? (colorEnabled ? COCKPIT_COLORS.secondary : undefined)
94
+ ? resolveInkColor(colorEnabled, COCKPIT_COLORS.brand)
73
95
  : item.current
74
- ? (colorEnabled ? COCKPIT_COLORS.muted : undefined)
96
+ ? resolveInkColor(colorEnabled, COCKPIT_COLORS.muted)
75
97
  : undefined;
76
98
  return React.createElement(Text, {
77
99
  key: item.id,
@@ -80,7 +102,7 @@ export function CockpitNav({ model, colorEnabled = true }) {
80
102
  }, `${item.marker} ${item.label}${suffix}`);
81
103
  }),
82
104
  model.explanation && React.createElement(Text, {
83
- color: COCKPIT_COLORS.muted
105
+ color: resolveInkColor(colorEnabled, COCKPIT_COLORS.muted)
84
106
  }, model.explanation)
85
107
  );
86
108
  }
@@ -90,7 +112,7 @@ function stripLabel(item, { compact }) {
90
112
  return item.label.split(/[&·]/)[0].trim().split(/\s+/)[0];
91
113
  }
92
114
 
93
- /** Horizontal navigation strip under the top bar. */
115
+ /** Segmented horizontal nav no border; active segment uses interactive. */
94
116
  export function CockpitNavStrip({
95
117
  model,
96
118
  colorEnabled = true,
@@ -101,18 +123,20 @@ export function CockpitNavStrip({
101
123
  const parts = model.items.map((item) => {
102
124
  const label = stripLabel(item, { compact });
103
125
  const suffix = item.current && !item.selected ? "*" : "";
104
- const color = item.focused
105
- ? (colorEnabled ? COCKPIT_COLORS.primary : undefined)
126
+ const active = item.focused || item.selected;
127
+ const color = item.focused || (focused && item.selected)
128
+ ? resolveInkColor(colorEnabled, COCKPIT_COLORS.interactive)
106
129
  : item.selected
107
- ? (colorEnabled ? COCKPIT_COLORS.secondary : undefined)
108
- : item.current
109
- ? (colorEnabled ? COCKPIT_COLORS.muted : undefined)
110
- : (colorEnabled ? COCKPIT_COLORS.muted : undefined);
130
+ ? resolveInkColor(colorEnabled, COCKPIT_COLORS.interactive)
131
+ : resolveInkColor(colorEnabled, COCKPIT_COLORS.muted);
132
+ const text = focused && item.selected
133
+ ? `[${label}]${suffix}`
134
+ : `${item.marker}${label}${suffix}`;
111
135
  return React.createElement(Text, {
112
136
  key: item.id,
113
- bold: item.focused || item.selected,
137
+ bold: active,
114
138
  color
115
- }, `${item.marker}${label}${suffix}`);
139
+ }, text);
116
140
  });
117
141
 
118
142
  const joined = [];
@@ -120,7 +144,7 @@ export function CockpitNavStrip({
120
144
  if (index > 0) {
121
145
  joined.push(React.createElement(Text, {
122
146
  key: `sep-${index}`,
123
- color: colorEnabled ? COCKPIT_COLORS.muted : undefined
147
+ color: resolveInkColor(colorEnabled, COCKPIT_COLORS.muted)
124
148
  }, " · "));
125
149
  }
126
150
  joined.push(node);
@@ -129,13 +153,12 @@ export function CockpitNavStrip({
129
153
  return React.createElement(Box, {
130
154
  flexDirection: "column",
131
155
  width: "100%",
132
- borderStyle: "single",
133
- borderColor: focused ? COCKPIT_COLORS.primary : COCKPIT_COLORS.muted,
134
- paddingX: 1
156
+ marginTop: 0,
157
+ marginBottom: 0
135
158
  },
136
159
  React.createElement(Box, { flexDirection: "row", flexWrap: "wrap" }, ...joined),
137
160
  model.explanation && React.createElement(Text, {
138
- color: COCKPIT_COLORS.muted
161
+ color: resolveInkColor(colorEnabled, COCKPIT_COLORS.muted)
139
162
  }, model.explanation)
140
163
  );
141
164
  }
@@ -145,7 +168,9 @@ export function CockpitSystemStrip({ model, colorEnabled = true }) {
145
168
  return React.createElement(Box, { flexDirection: "column" },
146
169
  model.rows.map((row) =>
147
170
  React.createElement(Text, { key: row.key },
148
- React.createElement(Text, { color: COCKPIT_COLORS.muted }, `${row.key.padEnd(7)}`),
171
+ React.createElement(Text, {
172
+ color: resolveInkColor(colorEnabled, COCKPIT_COLORS.muted)
173
+ }, `${row.key.padEnd(7)}`),
149
174
  React.createElement(CockpitBadge, {
150
175
  label: row.value,
151
176
  kind: row.kind,
@@ -156,23 +181,19 @@ export function CockpitSystemStrip({ model, colorEnabled = true }) {
156
181
  );
157
182
  }
158
183
 
159
- export function CockpitFooter({ model, columns = 80 }) {
184
+ /** Single-line shortcut bar no framed box. */
185
+ export function CockpitFooter({ model, columns = 80, colorEnabled = true }) {
160
186
  const width = Math.max(24, Math.min(Number(columns) || 80, 120));
161
- const bar = Math.max(20, width - 2);
162
- return React.createElement(Box, { flexDirection: "column", width: "100%" },
163
- React.createElement(Text, { color: COCKPIT_COLORS.muted },
164
- `├${"".repeat(bar)}┤`
165
- ),
166
- React.createElement(Text, { color: COCKPIT_COLORS.muted }, `│ ${model.text}`),
167
- React.createElement(Text, { color: COCKPIT_COLORS.muted },
168
- `╰${"─".repeat(bar)}╯`
169
- )
187
+ return React.createElement(Box, { width: "100%", marginTop: 1 },
188
+ React.createElement(Text, {
189
+ color: resolveInkColor(colorEnabled, COCKPIT_COLORS.muted)
190
+ }, String(model.text ?? "").slice(0, width))
170
191
  );
171
192
  }
172
193
 
173
194
  /**
174
195
  * Responsive single-panel shell: TopBar → NavStrip → Main → Footer.
175
- * SYSTEM side column removed (diagnostics via Enter/detail in later slices).
196
+ * No nested borders hierarchy via type and spacing.
176
197
  */
177
198
  export function CockpitShell({
178
199
  topBar,
@@ -196,8 +217,9 @@ export function CockpitShell({
196
217
  React.createElement(CockpitPanel, {
197
218
  title: undefined,
198
219
  focused: contentFocused,
199
- width: "100%"
220
+ width: "100%",
221
+ colorEnabled
200
222
  }, children),
201
- React.createElement(CockpitFooter, { model: footer, columns })
223
+ React.createElement(CockpitFooter, { model: footer, columns, colorEnabled })
202
224
  );
203
225
  }
@@ -143,6 +143,6 @@ export function formatChangesActionLines({
143
143
 
144
144
  export function buildChangesFooterParts(phase) {
145
145
  if (phase === CHANGES_PHASE.PREVIEWING || phase === CHANGES_PHASE.APPLYING) return ["Working…", "Esc Back"];
146
- if (phase === CHANGES_PHASE.CONFIRMING) return ["Y Apply", "N/Esc Cancel"];
147
- return ["A Preview", "R Re-scan", "Esc Back"];
146
+ if (phase === CHANGES_PHASE.CONFIRMING) return ["Y Apply", "N/Esc Cancel", "Space"];
147
+ return ["A Preview", "R Re-scan", "Space", "Esc Back"];
148
148
  }
@@ -1,5 +1,10 @@
1
1
  import { CONTROL_PLANE_HEALTH } from "../control-plane-snapshot.js";
2
2
  import { formatAlertsHeadline } from "./cockpit-alerts.js";
3
+ import {
4
+ adaptUsageModel,
5
+ formatMeasuredBudgets,
6
+ formatUsageLinesFromModel
7
+ } from "./cockpit-usage.js";
3
8
 
4
9
  export function buildControlCenterModel({
5
10
  projectName = "project",
@@ -112,73 +117,16 @@ function formatHealthLabel(kind) {
112
117
  }
113
118
 
114
119
  function formatTokenHeadline(budgets) {
115
- if (!budgets || typeof budgets !== "object") return "Data unavailable";
116
- const parts = [];
117
- if (Number.isFinite(budgets.stableUsedTokens) && Number.isFinite(budgets.stableBudgetTokens)) {
118
- parts.push(`stable ${budgets.stableUsedTokens}/${budgets.stableBudgetTokens}`);
119
- }
120
- if (Number.isFinite(budgets.requestUsedTokens) && Number.isFinite(budgets.requestBudgetTokens)) {
121
- parts.push(`request ${budgets.requestUsedTokens}/${budgets.requestBudgetTokens}`);
122
- }
123
- return parts.length > 0 ? parts.join(" · ") : "Data unavailable";
124
- }
125
-
126
- /**
127
- * Usage surface: measured budgets when present, configured profile limits,
128
- * and auditable run tokenUsage — never invent totals.
129
- */
130
- function resolveProfileLimits(dashboard) {
131
- const resolved = dashboard?.profile?.profile ?? {};
132
- const configured = [];
133
- if (Number.isFinite(resolved.tokenBudget)) configured.push(`token ${resolved.tokenBudget}`);
134
- if (Number.isFinite(resolved.stableContextBudget)) configured.push(`stable ${resolved.stableContextBudget}`);
135
- if (Number.isFinite(resolved.requestContextBudget)) configured.push(`request ${resolved.requestContextBudget}`);
136
- return configured;
120
+ return formatMeasuredBudgets(budgets) ?? "Data unavailable";
137
121
  }
138
122
 
139
- function hasFiniteUsage(usage) {
140
- if (!usage || typeof usage !== "object") return false;
141
- return Number.isFinite(usage.total)
142
- || Number.isFinite(usage.input)
143
- || Number.isFinite(usage.output);
144
- }
145
-
146
- function formatRunUsageLine(run) {
147
- const usage = run.tokenUsage;
148
- const parts = [];
149
- if (Number.isFinite(usage.input)) parts.push(`in ${usage.input}`);
150
- if (Number.isFinite(usage.output)) parts.push(`out ${usage.output}`);
151
- if (Number.isFinite(usage.total)) parts.push(`total ${usage.total}`);
152
- return `${run.agentId ?? "agent"} · ${parts.join(" · ")}`;
153
- }
154
-
155
- export function formatUsageLines({ snapshot = null, dashboard = null } = {}) {
156
- const lines = ["MEASURED"];
157
- const measured = formatTokenHeadline(snapshot?.budgets);
158
- lines.push(measured === "Data unavailable" ? "Data unavailable" : measured);
159
-
160
- const configured = resolveProfileLimits(dashboard);
161
- lines.push("", "CONFIGURED LIMITS");
162
- lines.push(configured.length > 0 ? configured.join(" · ") : "No profile token budgets configured.");
163
-
164
- const runs = [...(dashboard?.activeRuns ?? []), ...(dashboard?.recentRuns ?? [])]
165
- .filter((run) => hasFiniteUsage(run?.tokenUsage));
166
- lines.push("", "RUN USAGE");
167
- if (runs.length === 0) {
168
- lines.push("No auditable run tokenUsage yet.");
169
- } else {
170
- for (const run of runs.slice(0, 3)) {
171
- lines.push(formatRunUsageLine(run));
172
- }
173
- }
174
-
175
- lines.push("", "Auditable budgets only — no invented token savings.");
176
- return lines;
123
+ /** Usage surface via shared auditable model — never invent totals. */
124
+ export function formatUsageLines({
125
+ snapshot = null, dashboard = null, layoutMode = undefined
126
+ } = {}) {
127
+ return formatUsageLinesFromModel(adaptUsageModel({ snapshot, dashboard, layoutMode }));
177
128
  }
178
129
 
179
130
  export function hasAuditableUsage({ snapshot = null, dashboard = null } = {}) {
180
- if (formatTokenHeadline(snapshot?.budgets) !== "Data unavailable") return true;
181
- if (resolveProfileLimits(dashboard).length > 0) return true;
182
- return [...(dashboard?.activeRuns ?? []), ...(dashboard?.recentRuns ?? [])]
183
- .some((run) => hasFiniteUsage(run?.tokenUsage));
131
+ return adaptUsageModel({ snapshot, dashboard }).hasEvidence;
184
132
  }
@@ -35,7 +35,10 @@ export function createCockpitUiState({
35
35
  helpOpen = false,
36
36
  returnView = null,
37
37
  paletteOpen = false,
38
- paletteIndex = 0
38
+ paletteIndex = 0,
39
+ overviewDetailsOpen = false,
40
+ governanceDetailsOpen = false,
41
+ activityDetailsOpen = false
39
42
  } = {}) {
40
43
  const regions = regionsForLayout(layoutMode);
41
44
  const preferred = region ?? defaultRegionForView(view, layoutMode);
@@ -52,6 +55,9 @@ export function createCockpitUiState({
52
55
  returnView,
53
56
  paletteOpen,
54
57
  paletteIndex: Math.max(0, paletteIndex),
58
+ overviewDetailsOpen: Boolean(overviewDetailsOpen),
59
+ governanceDetailsOpen: Boolean(governanceDetailsOpen),
60
+ activityDetailsOpen: Boolean(activityDetailsOpen),
55
61
  shouldExit: false
56
62
  };
57
63
  }
@@ -145,7 +151,10 @@ export function reduceCockpitUi(state, action) {
145
151
  listIndex: 0,
146
152
  region: defaultRegionForView(item.view, state.layoutMode),
147
153
  helpOpen: false,
148
- returnView: null
154
+ returnView: null,
155
+ overviewDetailsOpen: false,
156
+ governanceDetailsOpen: false,
157
+ activityDetailsOpen: false
149
158
  });
150
159
  }
151
160
  case "set-view": {
@@ -159,9 +168,21 @@ export function reduceCockpitUi(state, action) {
159
168
  : clamp(action.navIndex, 0, COCKPIT_NAV.length - 1),
160
169
  region: action.region ?? defaultRegionForView(nextView, state.layoutMode),
161
170
  returnView: action.returnView ?? state.returnView,
162
- helpOpen: nextView === ORCHESTRATOR_VIEWS.HELP
171
+ helpOpen: nextView === ORCHESTRATOR_VIEWS.HELP,
172
+ overviewDetailsOpen: false,
173
+ governanceDetailsOpen: false,
174
+ activityDetailsOpen: false
163
175
  });
164
176
  }
177
+ case "toggle-overview-details":
178
+ if (state.view !== ORCHESTRATOR_VIEWS.HOME) return state;
179
+ return { ...state, overviewDetailsOpen: !state.overviewDetailsOpen };
180
+ case "toggle-governance-details":
181
+ if (state.view !== ORCHESTRATOR_VIEWS.CHANGES) return state;
182
+ return { ...state, governanceDetailsOpen: !state.governanceDetailsOpen };
183
+ case "toggle-activity-details":
184
+ if (state.view !== ORCHESTRATOR_VIEWS.ACTIVITY) return state;
185
+ return { ...state, activityDetailsOpen: !state.activityDetailsOpen };
165
186
  case "toggle-help":
166
187
  if (state.helpOpen || state.view === ORCHESTRATOR_VIEWS.HELP) {
167
188
  return goOverview(state);
@@ -170,12 +191,24 @@ export function reduceCockpitUi(state, action) {
170
191
  ...state,
171
192
  helpOpen: true,
172
193
  view: ORCHESTRATOR_VIEWS.HELP,
173
- region: defaultRegionForView(ORCHESTRATOR_VIEWS.HELP, state.layoutMode)
194
+ region: defaultRegionForView(ORCHESTRATOR_VIEWS.HELP, state.layoutMode),
195
+ overviewDetailsOpen: false,
196
+ governanceDetailsOpen: false,
197
+ activityDetailsOpen: false
174
198
  });
175
199
  case "escape": {
176
200
  if (state.paletteOpen) {
177
201
  return closePalette(state);
178
202
  }
203
+ if (state.view === ORCHESTRATOR_VIEWS.HOME && state.overviewDetailsOpen) {
204
+ return { ...state, overviewDetailsOpen: false };
205
+ }
206
+ if (state.view === ORCHESTRATOR_VIEWS.CHANGES && state.governanceDetailsOpen) {
207
+ return { ...state, governanceDetailsOpen: false };
208
+ }
209
+ if (state.view === ORCHESTRATOR_VIEWS.ACTIVITY && state.activityDetailsOpen) {
210
+ return { ...state, activityDetailsOpen: false };
211
+ }
179
212
  if (state.helpOpen || state.view === ORCHESTRATOR_VIEWS.HELP) {
180
213
  return goOverview(state);
181
214
  }
@@ -228,7 +261,10 @@ function goOverview(state) {
228
261
  navIndex: 0,
229
262
  listIndex: 0,
230
263
  region: defaultRegionForView(ORCHESTRATOR_VIEWS.HOME, state.layoutMode),
231
- returnView: null
264
+ returnView: null,
265
+ overviewDetailsOpen: false,
266
+ governanceDetailsOpen: false,
267
+ activityDetailsOpen: false
232
268
  });
233
269
  }
234
270
 
@@ -14,6 +14,7 @@ export function resolveEnterNavIntent({
14
14
  const isOverview = navItem.id === "overview" || navItem.view === ORCHESTRATOR_VIEWS.HOME;
15
15
  if (isOverview) {
16
16
  if (currentView === ORCHESTRATOR_VIEWS.HOME && ctaDestination) {
17
+ if (ctaDestination === "setup") return { kind: "activate-setup" };
17
18
  return { kind: "activate-cta", destination: ctaDestination };
18
19
  }
19
20
  return { kind: "open-nav", view: ORCHESTRATOR_VIEWS.HOME };