@kal-elsam/kairo-runtime 0.11.0 → 0.13.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/global-template/components/agent-skills/LICENSE +21 -0
- package/global-template/components/agent-skills/PROVENANCE.md +26 -0
- package/global-template/components/agent-skills/skills/context-engineering/SKILL.md +289 -0
- package/global-template/components/agent-skills/skills/frontend-ui-engineering/SKILL.md +328 -0
- package/global-template/components/agent-skills/skills/observability-and-instrumentation/SKILL.md +203 -0
- package/global-template/components/agent-skills/skills/performance-optimization/SKILL.md +396 -0
- package/global-template/components/agent-skills/skills/source-driven-development/SKILL.md +194 -0
- package/global-template/components/catalog.json +29 -0
- package/package.json +5 -2
- package/scripts/cockpit-smoke.mjs +2 -1
- package/src/cli.js +136 -8
- package/src/global/component-builders.js +3 -1
- package/src/global/components/agent-skills.js +27 -0
- package/src/global/ink/cockpit-control-center.js +107 -4
- package/src/global/ink/cockpit-scan.js +20 -2
- package/src/global/ink/ecosystem-updates-display.js +37 -0
- package/src/global/ink/launch-input.js +32 -1
- package/src/global/ink/obsidian-vault-display.js +37 -0
- package/src/global/ink/orchestrator-app.js +2 -1
- package/src/global/ink/orchestrator-state.js +17 -2
- package/src/global/ink/system-resources-display.js +109 -0
- package/src/global/ink/use-orchestrator-data.js +40 -4
- package/src/global/ink/ux/live-overview.js +11 -1
- package/src/global/mcp/kairo-mcp.js +230 -0
- package/src/global/observability/build-companion-snapshot.js +302 -0
- package/src/global/observability/build-observability-snapshot.js +24 -0
- package/src/global/observability/ecosystem-updates.js +224 -0
- package/src/global/observability/gentle-bundle-export.js +71 -0
- package/src/global/observability/gentle-bundle-import.js +122 -0
- package/src/global/observability/gentle-probe.js +155 -0
- package/src/global/observability/graphify-ops.js +133 -0
- package/src/global/observability/graphify-parse-cache.js +90 -0
- package/src/global/observability/graphify-probe.js +185 -0
- package/src/global/observability/hermes-activity.js +163 -0
- package/src/global/observability/hermes-probe.js +171 -0
- package/src/global/observability/index.js +124 -0
- package/src/global/observability/obsidian-knowledge-preview.js +214 -0
- package/src/global/observability/obsidian-knowledge-views.js +227 -0
- package/src/global/observability/obsidian-publisher.js +181 -0
- package/src/global/observability/obsidian-status.js +76 -0
- package/src/global/observability/obsidian-vault.js +259 -0
- package/src/global/observability/passive-snapshot-flight.js +93 -0
- package/src/global/observability/probe-contract.js +38 -0
- package/src/global/observability/probe-registry.js +30 -0
- package/src/global/observability/resource-advisor.js +71 -0
- package/src/global/observability/system-resources.js +171 -0
- package/src/global/runtime/alerts/alert-cli.js +31 -0
- package/src/global/runtime/alerts/alert-store.js +29 -6
- package/src/global/runtime/alerts/alert-validate.js +25 -1
- package/src/global/runtime/alerts/controlled-alert-actions.js +56 -0
- package/src/global/runtime/execution-adapters/claude.js +2 -1
- package/src/global/runtime/execution-adapters/codex.js +2 -1
- package/src/global/runtime/execution-adapters/create-execution-adapter.js +3 -14
- package/src/global/runtime/execution-adapters/cursor.js +2 -1
- package/src/global/runtime/execution-adapters/opencode.js +2 -1
- package/src/global/runtime/execution-adapters/pi.js +2 -1
- package/src/global/runtime/review/index.js +1 -1
- package/src/global/runtime/review/review-cli.js +113 -3
- package/src/global/runtime/review/review-git.js +142 -11
- package/src/global/runtime/review/review-patch.js +2 -0
- package/src/global/runtime/review/review-receipts.js +12 -7
- package/src/global/runtime/review/review-runner.js +2 -2
- package/src/global/runtime/review/review-types.js +8 -5
- package/src/global/runtime/review/review-validate.js +5 -1
- package/src/global/runtime/run-cli.js +2 -0
- package/src/global/runtime/run-manager.js +39 -18
- package/src/global/runtime/run-permissions.js +231 -0
- package/src/global/runtime/run-profile.js +2 -0
- package/src/global/runtime/run-supervisor.js +77 -37
- package/src/global/runtime/run-types.js +2 -0
- package/src/global/updates-cli.js +41 -0
|
@@ -5,13 +5,32 @@ import {
|
|
|
5
5
|
formatMeasuredBudgets,
|
|
6
6
|
formatUsageLinesFromModel
|
|
7
7
|
} from "./cockpit-usage.js";
|
|
8
|
+
import { LAYOUT_MODES } from "./layout.js";
|
|
9
|
+
import {
|
|
10
|
+
formatSystemResourcesLines,
|
|
11
|
+
formatResourceAdviceLines
|
|
12
|
+
} from "./system-resources-display.js";
|
|
13
|
+
import { formatEcosystemUpdateLines } from "./ecosystem-updates-display.js";
|
|
14
|
+
import { formatObsidianVaultLines } from "./obsidian-vault-display.js";
|
|
15
|
+
|
|
16
|
+
const HERMES_WIDE_SESSION_LIMIT = 3;
|
|
17
|
+
const HERMES_TITLE_MAX = 48;
|
|
18
|
+
|
|
19
|
+
export {
|
|
20
|
+
diskFreeTone,
|
|
21
|
+
formatSystemResourcesLines,
|
|
22
|
+
formatResourceAdviceLines
|
|
23
|
+
} from "./system-resources-display.js";
|
|
24
|
+
export { formatEcosystemUpdateLines } from "./ecosystem-updates-display.js";
|
|
25
|
+
export { formatObsidianVaultLines } from "./obsidian-vault-display.js";
|
|
8
26
|
|
|
9
27
|
export function buildControlCenterModel({
|
|
10
28
|
projectName = "project",
|
|
11
29
|
snapshot = null,
|
|
12
30
|
dashboard = null,
|
|
13
|
-
layoutMode =
|
|
14
|
-
alerts = null
|
|
31
|
+
layoutMode = LAYOUT_MODES.COMPACT,
|
|
32
|
+
alerts = null,
|
|
33
|
+
companion = null
|
|
15
34
|
} = {}) {
|
|
16
35
|
if (!snapshot) {
|
|
17
36
|
return {
|
|
@@ -49,7 +68,9 @@ export function buildControlCenterModel({
|
|
|
49
68
|
activity: { headline: "No activity yet" },
|
|
50
69
|
alerts: formatAlertsHeadline(alerts),
|
|
51
70
|
tokens: { headline: "Data unavailable" },
|
|
52
|
-
|
|
71
|
+
companion: null,
|
|
72
|
+
companionNextAction: null,
|
|
73
|
+
includeEmbeddedStatus: layoutMode !== LAYOUT_MODES.WIDE,
|
|
53
74
|
runsSecondaryHint: "Detail via Enter · / actions"
|
|
54
75
|
};
|
|
55
76
|
}
|
|
@@ -94,11 +115,93 @@ export function buildControlCenterModel({
|
|
|
94
115
|
},
|
|
95
116
|
alerts: formatAlertsHeadline(alerts),
|
|
96
117
|
tokens: { headline: formatTokenHeadline(snapshot.budgets) },
|
|
97
|
-
|
|
118
|
+
companion: formatCompanionOverlay(companion, layoutMode),
|
|
119
|
+
companionNextAction: companion?.nextSafeAction ?? null,
|
|
120
|
+
includeEmbeddedStatus: layoutMode !== LAYOUT_MODES.WIDE,
|
|
98
121
|
runsSecondaryHint: "Detail via Enter · / actions"
|
|
99
122
|
};
|
|
100
123
|
}
|
|
101
124
|
|
|
125
|
+
/**
|
|
126
|
+
* Observe-only Hermes activity lines for Cockpit overlay.
|
|
127
|
+
* Never includes session ids, baseUrl, diagnostics dumps, or control affordances.
|
|
128
|
+
*/
|
|
129
|
+
export function formatHermesActivityLines(activity, layoutMode = LAYOUT_MODES.COMPACT) {
|
|
130
|
+
if (activity == null || typeof activity !== "object") {
|
|
131
|
+
return ["Hermes · unavailable"];
|
|
132
|
+
}
|
|
133
|
+
const state = typeof activity.state === "string" && activity.state.length > 0
|
|
134
|
+
? activity.state
|
|
135
|
+
: "error";
|
|
136
|
+
if (state !== "available" && state !== "partial") {
|
|
137
|
+
return [`Hermes · ${state}`];
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const agg = activity.aggregates && typeof activity.aggregates === "object"
|
|
141
|
+
? activity.aggregates
|
|
142
|
+
: {};
|
|
143
|
+
const active = Number.isInteger(agg.activeCount) ? agg.activeCount : null;
|
|
144
|
+
const ended = Number.isInteger(agg.endedCount) ? agg.endedCount : null;
|
|
145
|
+
const bits = [`Hermes · ${state}`];
|
|
146
|
+
if (active != null) bits.push(`${active} active`);
|
|
147
|
+
if (layoutMode !== LAYOUT_MODES.MINIMAL && ended != null) {
|
|
148
|
+
bits.push(`${ended} ended`);
|
|
149
|
+
}
|
|
150
|
+
const lines = [bits.join(" · ")];
|
|
151
|
+
|
|
152
|
+
if (layoutMode !== LAYOUT_MODES.WIDE) return lines;
|
|
153
|
+
|
|
154
|
+
const sessions = Array.isArray(activity.sessions) ? activity.sessions : [];
|
|
155
|
+
for (const session of sessions.slice(0, HERMES_WIDE_SESSION_LIMIT)) {
|
|
156
|
+
const label = hermesSessionLabel(session);
|
|
157
|
+
if (label) lines.push(` · ${label}`);
|
|
158
|
+
}
|
|
159
|
+
if (agg.hasMore === true || sessions.length > HERMES_WIDE_SESSION_LIMIT) {
|
|
160
|
+
lines.push(" · … more sessions");
|
|
161
|
+
}
|
|
162
|
+
return lines;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function hermesSessionLabel(session) {
|
|
166
|
+
if (session == null || typeof session !== "object" || Array.isArray(session)) return null;
|
|
167
|
+
const title = typeof session.title === "string" && session.title.trim()
|
|
168
|
+
? session.title.trim()
|
|
169
|
+
: null;
|
|
170
|
+
const source = typeof session.source === "string" && session.source.trim()
|
|
171
|
+
? session.source.trim()
|
|
172
|
+
: null;
|
|
173
|
+
const head = (title ?? source ?? "untitled")
|
|
174
|
+
.replace(/[\r\n\t]+/g, " ")
|
|
175
|
+
.slice(0, HERMES_TITLE_MAX);
|
|
176
|
+
const flag = session.active === true ? "active" : "ended";
|
|
177
|
+
return `${head} · ${flag}`;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function formatCompanionOverlay(companion, layoutMode = LAYOUT_MODES.COMPACT) {
|
|
181
|
+
if (!companion) return null;
|
|
182
|
+
const g = companion.signals?.gentle?.state ?? "unknown";
|
|
183
|
+
const gy = companion.signals?.graphify;
|
|
184
|
+
const graphBit = gy?.graphStatus ? `/${gy.graphStatus}` : "";
|
|
185
|
+
const en = companion.engram?.status ?? "unknown";
|
|
186
|
+
const links = companion.links?.length ?? 0;
|
|
187
|
+
return {
|
|
188
|
+
ok: companion.ok !== false,
|
|
189
|
+
lines: [
|
|
190
|
+
`Gentle · ${g}`,
|
|
191
|
+
`Graphify · ${gy?.state ?? "unknown"}${graphBit}`,
|
|
192
|
+
`Engram · ${en}`,
|
|
193
|
+
`Soft links · ${links}`,
|
|
194
|
+
...formatHermesActivityLines(companion.signals?.hermes?.activity, layoutMode),
|
|
195
|
+
...formatSystemResourcesLines(companion.signals?.system?.resources, layoutMode),
|
|
196
|
+
...formatResourceAdviceLines(companion.signals?.system?.advice, layoutMode),
|
|
197
|
+
...formatEcosystemUpdateLines(companion.signals?.ecosystem?.updates, layoutMode),
|
|
198
|
+
...formatObsidianVaultLines(companion.signals?.obsidian?.vault, layoutMode)
|
|
199
|
+
],
|
|
200
|
+
links: companion.links ?? [],
|
|
201
|
+
error: companion.error ?? null
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
|
|
102
205
|
function formatHealthLabel(kind) {
|
|
103
206
|
switch (kind) {
|
|
104
207
|
case CONTROL_PLANE_HEALTH.NOT_CONFIGURED:
|
|
@@ -12,7 +12,8 @@ export async function loadCockpitScanBundle({
|
|
|
12
12
|
cliVersion,
|
|
13
13
|
buildDashboard,
|
|
14
14
|
buildDiagnostics,
|
|
15
|
-
buildSnapshot
|
|
15
|
+
buildSnapshot,
|
|
16
|
+
buildCompanion = null
|
|
16
17
|
}) {
|
|
17
18
|
const [dashboard, diagnostics, snapshot] = await Promise.all([
|
|
18
19
|
buildDashboard({ homeDir, workspaceRoot, cliVersion }),
|
|
@@ -26,7 +27,24 @@ export async function loadCockpitScanBundle({
|
|
|
26
27
|
...CONTROL_PLANE_AUTO_SCAN
|
|
27
28
|
})
|
|
28
29
|
]);
|
|
29
|
-
|
|
30
|
+
let companion = null;
|
|
31
|
+
if (typeof buildCompanion === "function") {
|
|
32
|
+
try {
|
|
33
|
+
companion = await buildCompanion({
|
|
34
|
+
homeDir, workspaceRoot, packageName, packageRoot, cliVersion, dashboard, snapshot
|
|
35
|
+
});
|
|
36
|
+
} catch (error) {
|
|
37
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
38
|
+
companion = {
|
|
39
|
+
ok: false, error: detail, signals: null, engram: null, links: [], alertsCount: null,
|
|
40
|
+
nextSafeAction: {
|
|
41
|
+
kind: "investigate", title: "Investigate companion diagnostics",
|
|
42
|
+
detail, secondary: true, displayOnly: true
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return { dashboard, diagnostics, snapshot, companion };
|
|
30
48
|
}
|
|
31
49
|
|
|
32
50
|
/**
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { LAYOUT_MODES } from "./layout.js";
|
|
2
|
+
|
|
3
|
+
/** Display-only ecosystem update lines for Cockpit companion overlay. */
|
|
4
|
+
export function formatEcosystemUpdateLines(updates, layoutMode = LAYOUT_MODES.COMPACT) {
|
|
5
|
+
if (updates == null || typeof updates !== "object") return ["Updates · unavailable"];
|
|
6
|
+
const state = typeof updates.state === "string" && updates.state ? updates.state : "error";
|
|
7
|
+
const tools = updates.tools && typeof updates.tools === "object" ? updates.tools : {};
|
|
8
|
+
const pending = ["kairo", "hermes", "gentle", "skills"]
|
|
9
|
+
.map((id) => tools[id])
|
|
10
|
+
.filter((t) => t && t.updateAvailable === true);
|
|
11
|
+
const cache = updates.cacheHit === true ? " · cache" : "";
|
|
12
|
+
|
|
13
|
+
if (state !== "available" && state !== "partial" && pending.length === 0) {
|
|
14
|
+
return [`Updates · ${state}${cache}`];
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const lines = [
|
|
18
|
+
pending.length > 0
|
|
19
|
+
? `Updates · ${pending.length} available${cache}`
|
|
20
|
+
: `Updates · current${cache}`
|
|
21
|
+
];
|
|
22
|
+
if (layoutMode === LAYOUT_MODES.MINIMAL) return lines;
|
|
23
|
+
|
|
24
|
+
for (const tool of pending.slice(0, layoutMode === LAYOUT_MODES.WIDE ? 4 : 2)) {
|
|
25
|
+
const inst = tool.installed ?? "—";
|
|
26
|
+
const latest = tool.latest ?? "—";
|
|
27
|
+
lines.push(` · ${tool.id} ${inst} → ${latest}`);
|
|
28
|
+
}
|
|
29
|
+
if (pending.length === 0 && layoutMode === LAYOUT_MODES.WIDE) {
|
|
30
|
+
for (const id of ["kairo", "hermes", "gentle", "skills"]) {
|
|
31
|
+
const tool = tools[id];
|
|
32
|
+
if (!tool) continue;
|
|
33
|
+
lines.push(` · ${id} · ${tool.state ?? "unknown"}`);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return lines;
|
|
37
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
LAUNCH_PERMISSION_OPTIONS,
|
|
3
3
|
LAUNCH_WIZARD_STEPS,
|
|
4
|
+
advanceLaunchWizardStep,
|
|
4
5
|
retreatLaunchWizardStep
|
|
5
6
|
} from "./orchestrator-state.js";
|
|
6
7
|
|
|
@@ -92,7 +93,19 @@ export function handleLaunchInput(ctx) {
|
|
|
92
93
|
|
|
93
94
|
if (launchStep === LAUNCH_WIZARD_STEPS.CONFIRM) {
|
|
94
95
|
if (key.return) {
|
|
95
|
-
|
|
96
|
+
const next = advanceLaunchWizardStep(LAUNCH_WIZARD_STEPS.CONFIRM, {
|
|
97
|
+
permissionIndex: launchPermissionIndex
|
|
98
|
+
});
|
|
99
|
+
if (next === LAUNCH_WIZARD_STEPS.UNSAFE_CONFIRM) {
|
|
100
|
+
setLaunchStep(LAUNCH_WIZARD_STEPS.UNSAFE_CONFIRM);
|
|
101
|
+
return true;
|
|
102
|
+
}
|
|
103
|
+
handleLaunch({
|
|
104
|
+
...launchDraft,
|
|
105
|
+
permissionIndex: launchPermissionIndex,
|
|
106
|
+
allowUnsafePermissions: false,
|
|
107
|
+
permissionSource: "cockpit"
|
|
108
|
+
});
|
|
96
109
|
return true;
|
|
97
110
|
}
|
|
98
111
|
if (key.escape) {
|
|
@@ -101,6 +114,24 @@ export function handleLaunchInput(ctx) {
|
|
|
101
114
|
}
|
|
102
115
|
}
|
|
103
116
|
|
|
117
|
+
if (launchStep === LAUNCH_WIZARD_STEPS.UNSAFE_CONFIRM) {
|
|
118
|
+
const answer = String(inputKey ?? "").toLowerCase();
|
|
119
|
+
if (answer === "y") {
|
|
120
|
+
handleLaunch({
|
|
121
|
+
...launchDraft,
|
|
122
|
+
permissionIndex: launchPermissionIndex,
|
|
123
|
+
allowUnsafePermissions: true,
|
|
124
|
+
permissionSource: "cockpit"
|
|
125
|
+
});
|
|
126
|
+
return true;
|
|
127
|
+
}
|
|
128
|
+
if (answer === "n" || key.escape) {
|
|
129
|
+
setLaunchStep(LAUNCH_WIZARD_STEPS.CONFIRM);
|
|
130
|
+
return allowEscapeRetreat && key.escape ? "retreated" : true;
|
|
131
|
+
}
|
|
132
|
+
return true;
|
|
133
|
+
}
|
|
134
|
+
|
|
104
135
|
if (inputKey.toLowerCase() === "r") {
|
|
105
136
|
reload().catch(() => {});
|
|
106
137
|
return true;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { LAYOUT_MODES } from "./layout.js";
|
|
2
|
+
|
|
3
|
+
/** Display-only Obsidian vault lines — never shows paths, write CTAs, or sync controls. */
|
|
4
|
+
export function formatObsidianVaultLines(status, layoutMode = LAYOUT_MODES.COMPACT) {
|
|
5
|
+
if (status == null || typeof status !== "object") return ["Obsidian · unavailable"];
|
|
6
|
+
const state = typeof status.state === "string" && status.state ? status.state : "error";
|
|
7
|
+
const notes = typeof status.noteCount === "number" && Number.isFinite(status.noteCount)
|
|
8
|
+
? status.noteCount
|
|
9
|
+
: 0;
|
|
10
|
+
const pending = typeof status.pendingProposals === "number" && status.pendingProposals > 0
|
|
11
|
+
? status.pendingProposals
|
|
12
|
+
: 0;
|
|
13
|
+
|
|
14
|
+
if (state === "unconfigured") return ["Obsidian · unconfigured"];
|
|
15
|
+
if (state === "missing") return ["Obsidian · missing"];
|
|
16
|
+
if (state !== "available" && state !== "partial") {
|
|
17
|
+
return [`Obsidian · ${state}`];
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const head = state === "partial"
|
|
21
|
+
? `Obsidian · partial · ${notes} notes`
|
|
22
|
+
: `Obsidian · ${notes} notes`;
|
|
23
|
+
const lines = [head];
|
|
24
|
+
if (layoutMode === LAYOUT_MODES.MINIMAL) return lines;
|
|
25
|
+
|
|
26
|
+
if (pending > 0) lines.push(` · pending · ${pending}`);
|
|
27
|
+
else if (layoutMode === LAYOUT_MODES.COMPACT) lines.push(" · no auto-sync");
|
|
28
|
+
|
|
29
|
+
if (layoutMode === LAYOUT_MODES.WIDE) {
|
|
30
|
+
const pub = typeof status.lastPublishAt === "string" && status.lastPublishAt
|
|
31
|
+
? status.lastPublishAt.slice(0, 19)
|
|
32
|
+
: "never";
|
|
33
|
+
lines.push(` · last publish · ${pub}`);
|
|
34
|
+
if (pending === 0) lines.push(" · no auto-sync");
|
|
35
|
+
}
|
|
36
|
+
return lines;
|
|
37
|
+
}
|
|
@@ -502,7 +502,8 @@ export function OrchestratorApp({
|
|
|
502
502
|
snapshot: data.snapshot,
|
|
503
503
|
dashboard: data.dashboard,
|
|
504
504
|
layoutMode: mode,
|
|
505
|
-
alerts: data.alerts
|
|
505
|
+
alerts: data.alerts,
|
|
506
|
+
companion: data.companion
|
|
506
507
|
});
|
|
507
508
|
const systemOnline = data.snapshot
|
|
508
509
|
? data.snapshot.health !== CONTROL_PLANE_HEALTH.NOT_CONFIGURED
|
|
@@ -47,7 +47,8 @@ export const LAUNCH_WIZARD_STEPS = {
|
|
|
47
47
|
TASK: "task",
|
|
48
48
|
MODEL: "model",
|
|
49
49
|
PERMISSIONS: "permissions",
|
|
50
|
-
CONFIRM: "confirm"
|
|
50
|
+
CONFIRM: "confirm",
|
|
51
|
+
UNSAFE_CONFIRM: "unsafe-confirm"
|
|
51
52
|
};
|
|
52
53
|
|
|
53
54
|
export const LAUNCH_PERMISSION_OPTIONS = [
|
|
@@ -75,7 +76,7 @@ export function resolveLaunchPermissions(draft) {
|
|
|
75
76
|
return LAUNCH_PERMISSION_OPTIONS[draft.permissionIndex]?.permissions ?? [];
|
|
76
77
|
}
|
|
77
78
|
|
|
78
|
-
export function advanceLaunchWizardStep(currentStep) {
|
|
79
|
+
export function advanceLaunchWizardStep(currentStep, { permissionIndex = 0 } = {}) {
|
|
79
80
|
switch (currentStep) {
|
|
80
81
|
case LAUNCH_WIZARD_STEPS.AGENT:
|
|
81
82
|
return LAUNCH_WIZARD_STEPS.TASK;
|
|
@@ -85,6 +86,10 @@ export function advanceLaunchWizardStep(currentStep) {
|
|
|
85
86
|
return LAUNCH_WIZARD_STEPS.PERMISSIONS;
|
|
86
87
|
case LAUNCH_WIZARD_STEPS.PERMISSIONS:
|
|
87
88
|
return LAUNCH_WIZARD_STEPS.CONFIRM;
|
|
89
|
+
case LAUNCH_WIZARD_STEPS.CONFIRM: {
|
|
90
|
+
const perms = resolveLaunchPermissions({ permissionIndex });
|
|
91
|
+
return perms.length > 0 ? LAUNCH_WIZARD_STEPS.UNSAFE_CONFIRM : LAUNCH_WIZARD_STEPS.CONFIRM;
|
|
92
|
+
}
|
|
88
93
|
default:
|
|
89
94
|
return LAUNCH_WIZARD_STEPS.CONFIRM;
|
|
90
95
|
}
|
|
@@ -92,6 +97,8 @@ export function advanceLaunchWizardStep(currentStep) {
|
|
|
92
97
|
|
|
93
98
|
export function retreatLaunchWizardStep(currentStep) {
|
|
94
99
|
switch (currentStep) {
|
|
100
|
+
case LAUNCH_WIZARD_STEPS.UNSAFE_CONFIRM:
|
|
101
|
+
return LAUNCH_WIZARD_STEPS.CONFIRM;
|
|
95
102
|
case LAUNCH_WIZARD_STEPS.CONFIRM:
|
|
96
103
|
return LAUNCH_WIZARD_STEPS.PERMISSIONS;
|
|
97
104
|
case LAUNCH_WIZARD_STEPS.PERMISSIONS:
|
|
@@ -147,6 +154,14 @@ export function formatLaunchWizardLines({
|
|
|
147
154
|
return lines;
|
|
148
155
|
}
|
|
149
156
|
|
|
157
|
+
if (step === LAUNCH_WIZARD_STEPS.UNSAFE_CONFIRM) {
|
|
158
|
+
const label = LAUNCH_PERMISSION_OPTIONS[permissionIndex]?.label ?? "unsafe";
|
|
159
|
+
lines.push(`Unsafe mode selected: ${label}`);
|
|
160
|
+
lines.push("This skips agent permission prompts for this run only.");
|
|
161
|
+
lines.push("Press Y to confirm unsafe launch · N/Esc to go back");
|
|
162
|
+
return lines;
|
|
163
|
+
}
|
|
164
|
+
|
|
150
165
|
lines.push(`Agent: ${draft.agentId ?? "—"}`);
|
|
151
166
|
lines.push(`Task length: ${draft.task.length} chars (content not stored)`);
|
|
152
167
|
lines.push(`Model: ${draft.model || "default"}`);
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { LAYOUT_MODES } from "./layout.js";
|
|
2
|
+
|
|
3
|
+
/** Disk free% tone — never invents a percent. */
|
|
4
|
+
export function diskFreeTone(freePercent) {
|
|
5
|
+
if (typeof freePercent !== "number" || !Number.isFinite(freePercent)) return null;
|
|
6
|
+
if (freePercent < 10) return "critical";
|
|
7
|
+
if (freePercent < 20) return "warning";
|
|
8
|
+
return "healthy";
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function formatBytesShort(bytes) {
|
|
12
|
+
if (typeof bytes !== "number" || !Number.isFinite(bytes) || bytes < 0) return null;
|
|
13
|
+
const gib = bytes / (1024 ** 3);
|
|
14
|
+
if (gib >= 1) return `${Math.round(gib * 10) / 10}G`;
|
|
15
|
+
const mib = bytes / (1024 ** 2);
|
|
16
|
+
return `${Math.round(mib)}M`;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function pctLabel(value, suffix = "% free") {
|
|
20
|
+
if (typeof value !== "number" || !Number.isFinite(value)) return null;
|
|
21
|
+
return `${value}${suffix}`;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Display-only System resource lines for Cockpit companion overlay.
|
|
26
|
+
* Never invents metrics; never includes paths, args, or control affordances.
|
|
27
|
+
*/
|
|
28
|
+
export function formatSystemResourcesLines(resources, layoutMode = LAYOUT_MODES.COMPACT) {
|
|
29
|
+
if (resources == null || typeof resources !== "object") {
|
|
30
|
+
return ["System · unavailable"];
|
|
31
|
+
}
|
|
32
|
+
const state = typeof resources.state === "string" && resources.state.length > 0
|
|
33
|
+
? resources.state
|
|
34
|
+
: "error";
|
|
35
|
+
if (state !== "available" && state !== "partial") {
|
|
36
|
+
return [`System · ${state}`];
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const ram = pctLabel(resources.memory?.freePercent);
|
|
40
|
+
const swapUsed = formatBytesShort(resources.swap?.usedBytes);
|
|
41
|
+
const diskPct = resources.disk?.freePercent;
|
|
42
|
+
const disk = pctLabel(diskPct);
|
|
43
|
+
const tone = diskFreeTone(diskPct);
|
|
44
|
+
|
|
45
|
+
const bits = ["System"];
|
|
46
|
+
bits.push(ram ? `RAM ${ram}` : "RAM n/a");
|
|
47
|
+
bits.push(swapUsed ? `Swap ${swapUsed} used` : "Swap n/a");
|
|
48
|
+
bits.push(disk ? `Disk ${disk}` : "Disk n/a");
|
|
49
|
+
if (tone && tone !== "healthy") bits.push(tone);
|
|
50
|
+
|
|
51
|
+
const lines = [bits.join(" · ")];
|
|
52
|
+
if (layoutMode === LAYOUT_MODES.MINIMAL) return lines;
|
|
53
|
+
|
|
54
|
+
const ramFree = resources.memory?.freePercent;
|
|
55
|
+
const memoryPressure = typeof ramFree === "number" && Number.isFinite(ramFree) && ramFree < 15;
|
|
56
|
+
if (memoryPressure) {
|
|
57
|
+
lines.push(" · Memory pressure · swap elevated relevance");
|
|
58
|
+
} else {
|
|
59
|
+
lines.push(" · Swap informational");
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const proc = resources.processes && typeof resources.processes === "object"
|
|
63
|
+
? resources.processes
|
|
64
|
+
: null;
|
|
65
|
+
if (proc) {
|
|
66
|
+
const total = Number.isInteger(proc.totalCount) ? proc.totalCount : null;
|
|
67
|
+
const zombies = Number.isInteger(proc.zombieCount) ? proc.zombieCount : null;
|
|
68
|
+
if (total != null && zombies != null) {
|
|
69
|
+
lines.push(` · Processes ${total} · zombies ${zombies}`);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (layoutMode !== LAYOUT_MODES.WIDE) return lines;
|
|
74
|
+
|
|
75
|
+
const tracked = Array.isArray(proc?.tracked) ? proc.tracked : [];
|
|
76
|
+
for (const entry of tracked.slice(0, 4)) {
|
|
77
|
+
if (entry == null || typeof entry !== "object") continue;
|
|
78
|
+
if (typeof entry.name !== "string" || !Number.isInteger(entry.count)) continue;
|
|
79
|
+
lines.push(` · ${entry.name} ×${entry.count}`);
|
|
80
|
+
}
|
|
81
|
+
const thermal = resources.thermal?.state ?? "unavailable";
|
|
82
|
+
const ssd = resources.ssdWear?.state ?? "unavailable";
|
|
83
|
+
lines.push(` · Thermal · ${thermal}`);
|
|
84
|
+
lines.push(` · SSD wear · ${ssd}`);
|
|
85
|
+
return lines;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/** Display-only advisor lines from deterministic recommendations. */
|
|
89
|
+
export function formatResourceAdviceLines(advice, layoutMode = LAYOUT_MODES.COMPACT) {
|
|
90
|
+
const list = Array.isArray(advice?.recommendations) ? advice.recommendations : [];
|
|
91
|
+
if (list.length === 0) return ["Advisor · quiet"];
|
|
92
|
+
const top = list[0];
|
|
93
|
+
const severity = typeof top.severity === "string" ? top.severity : "info";
|
|
94
|
+
const title = typeof top.title === "string" ? top.title : "recommendation";
|
|
95
|
+
const lines = [`Advisor · ${severity} · ${title}`];
|
|
96
|
+
if (layoutMode === LAYOUT_MODES.MINIMAL) return lines;
|
|
97
|
+
if (typeof top.detail === "string" && top.detail.length > 0) {
|
|
98
|
+
lines.push(` · ${top.detail.slice(0, 96)}`);
|
|
99
|
+
}
|
|
100
|
+
if (layoutMode === LAYOUT_MODES.WIDE) {
|
|
101
|
+
for (const item of list.slice(1, 3)) {
|
|
102
|
+
if (item == null || typeof item !== "object") continue;
|
|
103
|
+
const sev = typeof item.severity === "string" ? item.severity : "info";
|
|
104
|
+
const t = typeof item.title === "string" ? item.title : "recommendation";
|
|
105
|
+
lines.push(` · ${sev} · ${t}`);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return lines;
|
|
109
|
+
}
|
|
@@ -40,7 +40,14 @@ import {
|
|
|
40
40
|
} from "./cockpit-settings.js";
|
|
41
41
|
import { listReviewReceipts } from "../runtime/review/review-receipts.js";
|
|
42
42
|
import { assertReceiptSecretFree } from "../runtime/review/review-validate.js";
|
|
43
|
-
import { listAlerts
|
|
43
|
+
import { listAlerts } from "../runtime/alerts/alert-store.js";
|
|
44
|
+
import {
|
|
45
|
+
controlledDismissAlert, controlledResolveAlert
|
|
46
|
+
} from "../runtime/alerts/controlled-alert-actions.js";
|
|
47
|
+
import { buildCompanionSnapshot } from "../observability/build-companion-snapshot.js";
|
|
48
|
+
import { resolveGitHeadSha } from "../observability/graphify-probe.js";
|
|
49
|
+
import { runPassiveObservabilitySnapshot } from "../observability/passive-snapshot-flight.js";
|
|
50
|
+
import { inspectEngramIntegration } from "../integrations/engram-evidence.js";
|
|
44
51
|
|
|
45
52
|
export function useOrchestratorData({
|
|
46
53
|
homeDir,
|
|
@@ -56,6 +63,7 @@ export function useOrchestratorData({
|
|
|
56
63
|
const [dashboard, setDashboard] = useState(null);
|
|
57
64
|
const [diagnostics, setDiagnostics] = useState(null);
|
|
58
65
|
const [snapshot, setSnapshot] = useState(null);
|
|
66
|
+
const [companion, setCompanion] = useState(null);
|
|
59
67
|
const [selectedRun, setSelectedRun] = useState(null);
|
|
60
68
|
const [selectedEvents, setSelectedEvents] = useState([]);
|
|
61
69
|
const [reviews, setReviews] = useState([]);
|
|
@@ -78,7 +86,24 @@ export function useOrchestratorData({
|
|
|
78
86
|
cliVersion,
|
|
79
87
|
buildDashboard: buildRuntimeDashboardData,
|
|
80
88
|
buildDiagnostics: buildReadOnlyDiagnostics,
|
|
81
|
-
buildSnapshot: buildControlPlaneSnapshot
|
|
89
|
+
buildSnapshot: buildControlPlaneSnapshot,
|
|
90
|
+
buildCompanion: async ({ dashboard, snapshot: snap }) => buildCompanionSnapshot({
|
|
91
|
+
controlPlaneHealth: snap?.health ?? null,
|
|
92
|
+
runs: dashboard?.recentRuns ?? [],
|
|
93
|
+
inspectEngram: (ctx) => inspectEngramIntegration({
|
|
94
|
+
env: ctx?.env ?? process.env,
|
|
95
|
+
homeDir: ctx?.homeDir ?? homeDir
|
|
96
|
+
}),
|
|
97
|
+
buildObservability: (ctx) => runPassiveObservabilitySnapshot(ctx, {
|
|
98
|
+
force: Boolean(ctx?.force)
|
|
99
|
+
}),
|
|
100
|
+
observabilityContext: {
|
|
101
|
+
cwd: workspaceRoot, workspaceRoot, env: process.env,
|
|
102
|
+
headSha: resolveGitHeadSha(workspaceRoot)
|
|
103
|
+
},
|
|
104
|
+
loadReviews: async () => listReviewReceipts({ homeDir, limit: 20 }),
|
|
105
|
+
loadAlerts: async () => listAlerts({ homeDir, limit: 50 })
|
|
106
|
+
})
|
|
82
107
|
})), [homeDir, workspaceRoot, packageName, packageRoot, cliVersion]);
|
|
83
108
|
|
|
84
109
|
const reload = async ({ showLoading = false, asRetry = false } = {}) => {
|
|
@@ -98,6 +123,7 @@ export function useOrchestratorData({
|
|
|
98
123
|
setDashboard(outcome.result.dashboard);
|
|
99
124
|
setDiagnostics(outcome.result.diagnostics);
|
|
100
125
|
setSnapshot(outcome.result.snapshot);
|
|
126
|
+
setCompanion(outcome.result.companion ?? null);
|
|
101
127
|
try {
|
|
102
128
|
setAlerts(await listAlerts({ homeDir, limit: 50 }));
|
|
103
129
|
} catch {
|
|
@@ -166,8 +192,14 @@ export function useOrchestratorData({
|
|
|
166
192
|
if (!alert) return;
|
|
167
193
|
setBusy(true);
|
|
168
194
|
try {
|
|
169
|
-
|
|
170
|
-
|
|
195
|
+
const result = action === "dismiss"
|
|
196
|
+
? await controlledDismissAlert({
|
|
197
|
+
alertId: alert.alertId, confirmed: true, source: "cockpit", homeDir
|
|
198
|
+
})
|
|
199
|
+
: await controlledResolveAlert({
|
|
200
|
+
alertId: alert.alertId, confirmed: true, source: "cockpit", homeDir
|
|
201
|
+
});
|
|
202
|
+
if (!result.ok) throw new Error(result.message ?? result.code);
|
|
171
203
|
setAlerts(await listAlerts({ homeDir, limit: 50 }));
|
|
172
204
|
setStatusMessage(action === "dismiss" ? "Alert dismissed" : "Alert resolved");
|
|
173
205
|
} catch (error) {
|
|
@@ -197,6 +229,9 @@ export function useOrchestratorData({
|
|
|
197
229
|
cwd: workspaceRoot,
|
|
198
230
|
model: draft.model.trim() || null,
|
|
199
231
|
permissions,
|
|
232
|
+
allowUnsafePermissions: Boolean(draft.allowUnsafePermissions),
|
|
233
|
+
permissionSource: draft.permissionSource ?? "cockpit",
|
|
234
|
+
permissionConsentType: draft.allowUnsafePermissions ? "cockpit-unsafe-confirm" : null,
|
|
200
235
|
cliVersion,
|
|
201
236
|
profile: profile ?? null,
|
|
202
237
|
follow: false,
|
|
@@ -424,6 +459,7 @@ export function useOrchestratorData({
|
|
|
424
459
|
dashboard,
|
|
425
460
|
diagnostics,
|
|
426
461
|
snapshot,
|
|
462
|
+
companion,
|
|
427
463
|
selectedRun,
|
|
428
464
|
setSelectedRun,
|
|
429
465
|
selectedEvents,
|
|
@@ -55,6 +55,10 @@ export function buildOverviewDetails(model = {}) {
|
|
|
55
55
|
if (typeof model.alerts?.count === "number") {
|
|
56
56
|
lines.push(`Open alerts · ${model.alerts.count}`);
|
|
57
57
|
}
|
|
58
|
+
const secondary = model.companionNextAction;
|
|
59
|
+
if (secondary?.title && secondary.kind !== "idle") {
|
|
60
|
+
lines.push(`Companion · ${secondary.title}`);
|
|
61
|
+
}
|
|
58
62
|
if (lines.length === 0) return ["No extra evidence beyond the metrics above."];
|
|
59
63
|
return lines;
|
|
60
64
|
}
|
|
@@ -62,10 +66,15 @@ export function buildOverviewDetails(model = {}) {
|
|
|
62
66
|
/**
|
|
63
67
|
* Pure adapter: buildControlCenterModel → semantic overview props.
|
|
64
68
|
* Callout / CTA / metrics never include paths or IDs.
|
|
69
|
+
* Primary action is always governance CTA — companion is secondary metrics/details only.
|
|
65
70
|
*/
|
|
66
71
|
export function adaptControlCenterToOverview(model = {}) {
|
|
67
72
|
const status = model.status ?? model.health ?? {};
|
|
68
73
|
const next = model.nextAction ?? model.cta ?? {};
|
|
74
|
+
const companionMetrics = (model.companion?.lines ?? []).map((label, i) => ({
|
|
75
|
+
id: `companion-${i}`,
|
|
76
|
+
label
|
|
77
|
+
}));
|
|
69
78
|
return {
|
|
70
79
|
title: model.title ?? "Overview",
|
|
71
80
|
callout: {
|
|
@@ -81,7 +90,8 @@ export function adaptControlCenterToOverview(model = {}) {
|
|
|
81
90
|
metrics: [
|
|
82
91
|
{ id: "activity", label: `Activity · ${model.activity?.headline ?? "Idle"}` },
|
|
83
92
|
{ id: "alerts", label: `Alerts · ${model.alerts?.headline ?? "Alert data unavailable"}` },
|
|
84
|
-
{ id: "tokens", label: `Tokens · ${model.tokens?.headline ?? "Data unavailable"}` }
|
|
93
|
+
{ id: "tokens", label: `Tokens · ${model.tokens?.headline ?? "Data unavailable"}` },
|
|
94
|
+
...companionMetrics
|
|
85
95
|
],
|
|
86
96
|
details: buildOverviewDetails(model)
|
|
87
97
|
};
|