@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.
- package/README.md +14 -8
- package/package.json +1 -1
- package/scripts/cockpit-smoke.mjs +3 -3
- package/scripts/ux-prototype-tty.mjs +9 -0
- package/src/global/control-plane-proposals.js +2 -1
- package/src/global/control-plane-snapshot.js +1 -1
- package/src/global/ink/brand/wordmark.js +50 -0
- package/src/global/ink/cockpit/primitives.js +72 -50
- package/src/global/ink/cockpit-changes.js +2 -2
- package/src/global/ink/cockpit-control-center.js +12 -64
- package/src/global/ink/cockpit-controller.js +41 -5
- package/src/global/ink/cockpit-enter.js +1 -0
- package/src/global/ink/cockpit-models.js +14 -7
- package/src/global/ink/cockpit-palette.js +18 -7
- package/src/global/ink/cockpit-recovery.js +9 -6
- package/src/global/ink/cockpit-usage.js +111 -0
- package/src/global/ink/cockpit-views.js +70 -97
- package/src/global/ink/orchestrator-app.js +44 -4
- package/src/global/ink/setup-app.js +55 -72
- package/src/global/ink/setup-state.js +16 -0
- package/src/global/ink/theme.js +40 -8
- package/src/global/ink/ux/live-activity.js +191 -0
- package/src/global/ink/ux/live-alerts.js +156 -0
- package/src/global/ink/ux/live-governance.js +191 -0
- package/src/global/ink/ux/live-orchestration.js +185 -0
- package/src/global/ink/ux/live-overview.js +175 -0
- package/src/global/ink/ux/live-settings.js +186 -0
- package/src/global/ink/ux/live-setup.js +160 -0
- package/src/global/ink/ux/live-usage.js +49 -0
- package/src/global/ink/ux/semantic.js +101 -0
- package/src/global/ink/ux/task-flow-app.js +85 -0
- package/src/global/ink/ux/task-flow.js +173 -0
- package/src/global/orchestrator.js +37 -20
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
/** Pure task-flow prototype: Home → Setup → Overview. Mock only — no writes. */
|
|
2
|
+
|
|
3
|
+
export const SCREENS = { HOME: "home", SETUP: "setup", OVERVIEW: "overview" };
|
|
4
|
+
export const FOCUS = { PRIMARY: "primary", LIST: "list", DETAILS: "details" };
|
|
5
|
+
export const SETUP_STEPS = [
|
|
6
|
+
{ id: "detect", label: "Detect agents" },
|
|
7
|
+
{ id: "select", label: "Select governance" },
|
|
8
|
+
{ id: "review", label: "Review changes" },
|
|
9
|
+
{ id: "confirm", label: "Confirm" },
|
|
10
|
+
{ id: "receipt", label: "Receipt" }
|
|
11
|
+
];
|
|
12
|
+
|
|
13
|
+
export function resolveLayout(c = 80, r = 24) {
|
|
14
|
+
if (c >= 120 && r >= 40) return "wide";
|
|
15
|
+
if (c < 80 || r < 24) return "minimal";
|
|
16
|
+
return "compact";
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function createTaskFlowState(init = {}) {
|
|
20
|
+
const columns = init.columns ?? 80;
|
|
21
|
+
const rows = init.rows ?? 24;
|
|
22
|
+
return {
|
|
23
|
+
screen: init.screen ?? SCREENS.HOME,
|
|
24
|
+
setupStep: init.setupStep ?? 0,
|
|
25
|
+
listIndex: init.listIndex ?? 0,
|
|
26
|
+
focus: init.focus ?? FOCUS.PRIMARY,
|
|
27
|
+
detailsOpen: Boolean(init.detailsOpen),
|
|
28
|
+
columns,
|
|
29
|
+
rows,
|
|
30
|
+
exited: Boolean(init.exited),
|
|
31
|
+
layout: resolveLayout(columns, rows)
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function buildHomeModel() {
|
|
36
|
+
return {
|
|
37
|
+
title: "Home",
|
|
38
|
+
callout: { tone: "warn", title: "Needs attention", body: "2 open alerts · monitor idle · 1 active run" },
|
|
39
|
+
primary: { id: "start-setup", label: "Start setup · Cursor drift", detail: "Opens guided setup. Stale hooks detected." },
|
|
40
|
+
secondary: [
|
|
41
|
+
{ id: "overview", label: "Open overview" },
|
|
42
|
+
{ id: "details", label: "Show details", hint: "paths · ids" }
|
|
43
|
+
],
|
|
44
|
+
details: ["project: agentic-harness", "path hidden until Details"]
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function buildSetupModel(stepIndex = 0) {
|
|
49
|
+
const step = SETUP_STEPS[stepIndex] ?? SETUP_STEPS[0];
|
|
50
|
+
const isConfirm = step.id === "confirm";
|
|
51
|
+
const isReceipt = step.id === "receipt";
|
|
52
|
+
return {
|
|
53
|
+
title: "Setup",
|
|
54
|
+
steps: SETUP_STEPS,
|
|
55
|
+
stepIndex,
|
|
56
|
+
callout: {
|
|
57
|
+
tone: "info",
|
|
58
|
+
title: step.label,
|
|
59
|
+
body: isReceipt ? "Prototype receipt — no files written." : "Enter advances · Esc leaves setup."
|
|
60
|
+
},
|
|
61
|
+
primary: { label: isReceipt ? "Open overview" : isConfirm ? "Confirm (no write)" : "Continue" },
|
|
62
|
+
confirm: isConfirm ? { summary: "Apply governance preview to Cursor hooks.", primaryLabel: "Confirm (no write)" } : null,
|
|
63
|
+
receipt: isReceipt ? { title: "Receipt", lines: ["status: simulated", "wrote: none", "planId: proto-1"] } : null,
|
|
64
|
+
details: [`step: ${step.id}`, "write: disabled"]
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function buildOverviewModel() {
|
|
69
|
+
return {
|
|
70
|
+
title: "Overview",
|
|
71
|
+
callout: { tone: "warn", title: "Attention first", body: "2 items need a decision before secondary metrics." },
|
|
72
|
+
primary: { label: "Back to home priority" },
|
|
73
|
+
metrics: [
|
|
74
|
+
{ id: "agents", label: "Protected agents · Cursor · Codex · Pi" },
|
|
75
|
+
{ id: "monitor", label: "Monitor · Disabled" },
|
|
76
|
+
{ id: "runs", label: "Active runs · 1" },
|
|
77
|
+
{ id: "usage", label: "Usage · Pi evidence only" }
|
|
78
|
+
],
|
|
79
|
+
details: ["alert: alt-1", "run: run-1"]
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export function modelForState(state) {
|
|
84
|
+
if (state.screen === SCREENS.SETUP) return buildSetupModel(state.setupStep);
|
|
85
|
+
if (state.screen === SCREENS.OVERVIEW) return buildOverviewModel();
|
|
86
|
+
return buildHomeModel();
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** Focus mark only when PRIMARY — list/details own their own markers. */
|
|
90
|
+
export function focusMarkFor(state, unicode = true) {
|
|
91
|
+
return state.focus === FOCUS.PRIMARY ? (unicode ? "›" : ">") : " ";
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** Exactly one primary surface; confirm steps own the action (no duplicate line). */
|
|
95
|
+
export function resolvePrimaryPresentation(model, state, unicode = true) {
|
|
96
|
+
const mark = focusMarkFor(state, unicode);
|
|
97
|
+
if (model.confirm) {
|
|
98
|
+
return { mode: "confirm", summary: model.confirm.summary, label: model.confirm.primaryLabel, mark };
|
|
99
|
+
}
|
|
100
|
+
return { mode: "primary", label: model.primary.label, detail: model.primary.detail ?? null, mark };
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export function keyHintsFor(state) {
|
|
104
|
+
const hints = [
|
|
105
|
+
{ keys: "↑↓", label: "Move" },
|
|
106
|
+
{ keys: "Enter", label: "Primary" },
|
|
107
|
+
{ keys: "Space", label: "Details" },
|
|
108
|
+
{ keys: "/", label: "Home" },
|
|
109
|
+
{ keys: "Esc", label: state.screen === SCREENS.SETUP ? "Home" : "Exit" }
|
|
110
|
+
];
|
|
111
|
+
return state.layout === "minimal" ? hints.slice(0, 3) : hints;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export function reduceTaskFlow(state, event) {
|
|
115
|
+
switch (event.type) {
|
|
116
|
+
case "resize": {
|
|
117
|
+
const columns = event.columns ?? state.columns;
|
|
118
|
+
const rows = event.rows ?? state.rows;
|
|
119
|
+
return { ...state, columns, rows, layout: resolveLayout(columns, rows) };
|
|
120
|
+
}
|
|
121
|
+
case "up": return moveList(state, -1);
|
|
122
|
+
case "down": return moveList(state, 1);
|
|
123
|
+
case "space": return { ...state, detailsOpen: !state.detailsOpen, focus: FOCUS.DETAILS };
|
|
124
|
+
case "slash":
|
|
125
|
+
return { ...state, screen: SCREENS.HOME, focus: FOCUS.PRIMARY, listIndex: 0, detailsOpen: false };
|
|
126
|
+
case "escape":
|
|
127
|
+
if (state.detailsOpen) return { ...state, detailsOpen: false, focus: FOCUS.PRIMARY };
|
|
128
|
+
if (state.screen === SCREENS.SETUP) {
|
|
129
|
+
return { ...state, screen: SCREENS.HOME, focus: FOCUS.PRIMARY, listIndex: 0 };
|
|
130
|
+
}
|
|
131
|
+
return { ...state, exited: true };
|
|
132
|
+
case "enter": return activate(state);
|
|
133
|
+
default: return state;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function listLength(state) {
|
|
138
|
+
if (state.screen === SCREENS.HOME) return 2;
|
|
139
|
+
if (state.screen === SCREENS.OVERVIEW) return 4;
|
|
140
|
+
return 0;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function moveList(state, delta) {
|
|
144
|
+
const len = listLength(state);
|
|
145
|
+
if (len <= 0) return { ...state, focus: FOCUS.PRIMARY };
|
|
146
|
+
if (state.focus !== FOCUS.LIST) {
|
|
147
|
+
return { ...state, listIndex: delta > 0 ? 0 : len - 1, focus: FOCUS.LIST };
|
|
148
|
+
}
|
|
149
|
+
return { ...state, listIndex: (state.listIndex + delta + len) % len, focus: FOCUS.LIST };
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function activate(state) {
|
|
153
|
+
if (state.screen === SCREENS.HOME) {
|
|
154
|
+
if (state.focus === FOCUS.LIST) {
|
|
155
|
+
const id = buildHomeModel().secondary[state.listIndex]?.id;
|
|
156
|
+
if (id === "overview") {
|
|
157
|
+
return { ...state, screen: SCREENS.OVERVIEW, focus: FOCUS.PRIMARY, listIndex: 0, detailsOpen: false };
|
|
158
|
+
}
|
|
159
|
+
if (id === "details") return { ...state, detailsOpen: !state.detailsOpen, focus: FOCUS.DETAILS };
|
|
160
|
+
}
|
|
161
|
+
return { ...state, screen: SCREENS.SETUP, setupStep: 0, focus: FOCUS.PRIMARY, listIndex: 0, detailsOpen: false };
|
|
162
|
+
}
|
|
163
|
+
if (state.screen === SCREENS.SETUP) {
|
|
164
|
+
if (state.setupStep >= SETUP_STEPS.length - 1) {
|
|
165
|
+
return { ...state, screen: SCREENS.OVERVIEW, focus: FOCUS.PRIMARY, listIndex: 0, detailsOpen: false };
|
|
166
|
+
}
|
|
167
|
+
return { ...state, setupStep: state.setupStep + 1, focus: FOCUS.PRIMARY, detailsOpen: false };
|
|
168
|
+
}
|
|
169
|
+
if (state.screen === SCREENS.OVERVIEW) {
|
|
170
|
+
return { ...state, screen: SCREENS.HOME, focus: FOCUS.PRIMARY, listIndex: 0, detailsOpen: false };
|
|
171
|
+
}
|
|
172
|
+
return state;
|
|
173
|
+
}
|
|
@@ -85,27 +85,44 @@ export async function runOrchestratorShell({
|
|
|
85
85
|
}
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
88
|
+
for (;;) {
|
|
89
|
+
const outcome = await runOrchestratorInkImpl({
|
|
90
|
+
homeDir,
|
|
91
|
+
workspaceRoot,
|
|
92
|
+
packageRoot,
|
|
93
|
+
packageName: packageManifest.name,
|
|
94
|
+
cliVersion: packageManifest.version,
|
|
95
|
+
hasGlobalState: hasConfiguredGlobalState(homeDir),
|
|
96
|
+
fullscreenSession: session
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
if (outcome.error) {
|
|
100
|
+
throw outcome.error;
|
|
101
|
+
}
|
|
101
102
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
103
|
+
if (outcome?.action === "setup") {
|
|
104
|
+
const mid = await runHarnessSetupImpl({
|
|
105
|
+
packageRoot,
|
|
106
|
+
packageName: packageManifest.name,
|
|
107
|
+
cliVersion: packageManifest.version,
|
|
108
|
+
homeDir,
|
|
109
|
+
workspaceRoot,
|
|
110
|
+
onboarding: false,
|
|
111
|
+
interactive: true,
|
|
112
|
+
fullscreenSession: session
|
|
113
|
+
});
|
|
114
|
+
if (!mid?.cancelled) setupOutcome = mid;
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return {
|
|
119
|
+
cancelled: Boolean(outcome.cancelled),
|
|
120
|
+
wrote: Boolean(setupOutcome && !setupOutcome.cancelled),
|
|
121
|
+
action: outcome.action ?? null,
|
|
122
|
+
initialMode,
|
|
123
|
+
setup: setupOutcome
|
|
124
|
+
};
|
|
125
|
+
}
|
|
109
126
|
} finally {
|
|
110
127
|
if (ownsSession) {
|
|
111
128
|
session.leave();
|