@growthub/cli 0.14.3 → 0.14.4
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/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/helper/apply/route.js +33 -1
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/CeoCockpit.jsx +532 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/HelperSidecar.jsx +36 -5
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/helper-commands.js +9 -1
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/globals.css +11 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/ceo-agent-teams.js +211 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/ceo-bootstrap-console.js +325 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/ceo-cockpit-console.js +206 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-patch-policy.js +2 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-schema.js +69 -0
- package/package.json +1 -1
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CEO cockpit projection — the governed "chief orchestrator" oversight lens
|
|
3
|
+
* over the existing agent-swarm fleet (GOVERNED_COCKPIT_ENTRY_POINT_PATTERN_V1
|
|
4
|
+
* + CEO_PRIMITIVE_COCKPIT_ROADMAP_V1).
|
|
5
|
+
*
|
|
6
|
+
* This is a PURE deriver — no React, no fetch, no fs, no config writes, no
|
|
7
|
+
* localStorage, no CSS. It takes the workspace config (and, optionally, the
|
|
8
|
+
* agent-outcomes receipt stream) and emits a low-entropy view-model the
|
|
9
|
+
* CeoCockpit component renders. It introduces NO new governed object, NO new
|
|
10
|
+
* API, NO new PATCH field: the CEO oversees the same `sandbox-environment`
|
|
11
|
+
* swarm-workflows the Background Tasks cockpit executes, and every "Open"
|
|
12
|
+
* routes back into that existing surface.
|
|
13
|
+
*
|
|
14
|
+
* Causation ITT shape (state -> eligibility -> guidance -> action): each swarm
|
|
15
|
+
* workflow is a "direct report"; its run evidence + execution eligibility
|
|
16
|
+
* derive a state, a human headline, and the single next action — and the fleet
|
|
17
|
+
* rolls those up into one "needs your attention" pick so the CEO always knows
|
|
18
|
+
* the next move without reading logs.
|
|
19
|
+
*
|
|
20
|
+
* Data sources (all already in the contract):
|
|
21
|
+
* - findSwarmRunRows(config) — the governed fleet
|
|
22
|
+
* - deriveSwarmWorkflowExecutionEligibility(entry) — the existing readiness gate
|
|
23
|
+
* - deriveSwarmRunProjection(row.lastResponse) — the existing run projection
|
|
24
|
+
* - workspace:agent-outcomes receipts (optional) — governance rollup only
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
import { deriveSwarmRunProjection } from "./orchestration-run-console.js";
|
|
28
|
+
import {
|
|
29
|
+
findSwarmRunRows,
|
|
30
|
+
deriveSwarmWorkflowExecutionEligibility,
|
|
31
|
+
} from "./workspace-swarm-proposal.js";
|
|
32
|
+
|
|
33
|
+
// Parse the latest persisted run record off a swarm row. Mirrors the
|
|
34
|
+
// SwarmRunCockpit fallback exactly — row.lastResponse is the durable record
|
|
35
|
+
// when run history has not been re-fetched.
|
|
36
|
+
function parseRowRecord(row) {
|
|
37
|
+
const raw = row?.lastResponse;
|
|
38
|
+
if (!raw) return null;
|
|
39
|
+
if (typeof raw === "object") return raw;
|
|
40
|
+
try {
|
|
41
|
+
return JSON.parse(String(raw));
|
|
42
|
+
} catch {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Map a report state to the inherited run-console dot variant grammar
|
|
48
|
+
// (ok | fail | active | pending | canceled) — no new visual vocabulary.
|
|
49
|
+
function variantForState(state) {
|
|
50
|
+
switch (state) {
|
|
51
|
+
case "completed": return "ok";
|
|
52
|
+
case "failing": return "fail";
|
|
53
|
+
case "running": return "active";
|
|
54
|
+
case "blocked": return "canceled";
|
|
55
|
+
case "never-run":
|
|
56
|
+
default: return "pending";
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Next-action label per state — the CEO's verb, never a hidden flag.
|
|
61
|
+
function nextActionLabel(state) {
|
|
62
|
+
switch (state) {
|
|
63
|
+
case "blocked": return "Fix execution target";
|
|
64
|
+
case "failing": return "Review failed run";
|
|
65
|
+
case "never-run": return "Open to launch";
|
|
66
|
+
case "running": return "Open running task";
|
|
67
|
+
case "completed":
|
|
68
|
+
default: return "Open task";
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Classify one swarm workflow into exactly one report state. Blocked
|
|
73
|
+
// (no runnable execution target) takes priority because nothing else is
|
|
74
|
+
// actionable until it clears — the same truth the Play gate enforces.
|
|
75
|
+
function classifyReport(eligibility, projection) {
|
|
76
|
+
if (!eligibility?.ready) return "blocked";
|
|
77
|
+
const status = projection?.status;
|
|
78
|
+
if (status === "failed") return "failing";
|
|
79
|
+
if (status === "running" || status === "executing") return "running";
|
|
80
|
+
if (status === "completed") return "completed";
|
|
81
|
+
return "never-run";
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Human one-liner for a report card — plain language, no jargon.
|
|
85
|
+
function headlineForState(state, eligibility, agentCount) {
|
|
86
|
+
switch (state) {
|
|
87
|
+
case "blocked":
|
|
88
|
+
return eligibility?.guidance || "Set an execution target before this can run.";
|
|
89
|
+
case "failing":
|
|
90
|
+
return "Last run failed — review the transcript and re-run.";
|
|
91
|
+
case "running":
|
|
92
|
+
return "Running now — open to watch progress.";
|
|
93
|
+
case "completed":
|
|
94
|
+
return `Completed · ${agentCount} agent${agentCount === 1 ? "" : "s"}.`;
|
|
95
|
+
case "never-run":
|
|
96
|
+
default:
|
|
97
|
+
return `Ready · ${agentCount} agent${agentCount === 1 ? "" : "s"} · not run yet.`;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Attention priority: a CEO looks at what is broken first, then what is
|
|
102
|
+
// blocked, then what has never shipped. Healthy fleets surface nothing.
|
|
103
|
+
const ATTENTION_PRIORITY = ["failing", "blocked", "never-run"];
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Build the CEO cockpit view-model.
|
|
107
|
+
*
|
|
108
|
+
* @param {object} args
|
|
109
|
+
* @param {object} args.workspaceConfig live workspace config (GET /api/workspace)
|
|
110
|
+
* @param {Array} [args.receipts] workspace:agent-outcomes stream (optional)
|
|
111
|
+
* @returns {object} view-model — see module header.
|
|
112
|
+
*/
|
|
113
|
+
export function deriveCeoCockpit({ workspaceConfig, receipts = [] } = {}) {
|
|
114
|
+
const entries = findSwarmRunRows(workspaceConfig);
|
|
115
|
+
const safeReceipts = Array.isArray(receipts) ? receipts : [];
|
|
116
|
+
|
|
117
|
+
const reports = entries.map((entry, index) => {
|
|
118
|
+
const eligibility = deriveSwarmWorkflowExecutionEligibility(entry);
|
|
119
|
+
const record = parseRowRecord(entry.row);
|
|
120
|
+
const projection = record ? deriveSwarmRunProjection(record) : null;
|
|
121
|
+
const state = classifyReport(eligibility, projection);
|
|
122
|
+
const agentCount = Number.isFinite(Number(projection?.agentCount))
|
|
123
|
+
? Number(projection.agentCount)
|
|
124
|
+
: Number(eligibility?.runnableNodeCount) || 0;
|
|
125
|
+
const name = String(entry.row?.Name || "").trim();
|
|
126
|
+
// Stable, collision-proof identity: object + row id (or Name) + index.
|
|
127
|
+
// Two workflows that share a Name still get distinct reportIds, so the
|
|
128
|
+
// attention filter and React keys never drop or merge a record.
|
|
129
|
+
const rowKey = String(entry.row?.id || name || `row-${index}`).trim();
|
|
130
|
+
const reportId = `${entry.objectId}::${rowKey}::${index}`;
|
|
131
|
+
const lastRun = projection
|
|
132
|
+
? {
|
|
133
|
+
status: projection.status,
|
|
134
|
+
runId: projection.runId || null,
|
|
135
|
+
totalTokens: projection.totalTokens ?? null,
|
|
136
|
+
totalTools: projection.totalTools ?? null,
|
|
137
|
+
elapsedMs: projection.elapsedMs ?? null,
|
|
138
|
+
}
|
|
139
|
+
: null;
|
|
140
|
+
|
|
141
|
+
return {
|
|
142
|
+
reportId,
|
|
143
|
+
objectId: entry.objectId,
|
|
144
|
+
name,
|
|
145
|
+
objectLabel: entry.objectLabel || null,
|
|
146
|
+
lifecycleStatus: String(entry.row?.lifecycleStatus || "draft"),
|
|
147
|
+
version: String(entry.row?.version || "1"),
|
|
148
|
+
agentCount,
|
|
149
|
+
state,
|
|
150
|
+
variant: variantForState(state),
|
|
151
|
+
readiness: {
|
|
152
|
+
ready: eligibility.ready,
|
|
153
|
+
status: eligibility.status,
|
|
154
|
+
missing: eligibility.missing,
|
|
155
|
+
guidance: eligibility.guidance,
|
|
156
|
+
adapter: eligibility.adapter,
|
|
157
|
+
agentHost: eligibility.agentHost,
|
|
158
|
+
},
|
|
159
|
+
lastRun,
|
|
160
|
+
headline: headlineForState(state, eligibility, agentCount),
|
|
161
|
+
nextAction: {
|
|
162
|
+
label: nextActionLabel(state),
|
|
163
|
+
// Routes through the EXISTING swarm-run artifact surface — the CEO
|
|
164
|
+
// cockpit never executes; it hands off to Background Tasks.
|
|
165
|
+
artifact: name ? { surface: "swarm-run", objectId: entry.objectId, name } : null,
|
|
166
|
+
},
|
|
167
|
+
};
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
const countOf = (state) => reports.filter((r) => r.state === state).length;
|
|
171
|
+
const fleet = {
|
|
172
|
+
total: reports.length,
|
|
173
|
+
runnable: reports.filter((r) => r.readiness.ready).length,
|
|
174
|
+
blocked: countOf("blocked"),
|
|
175
|
+
failing: countOf("failing"),
|
|
176
|
+
neverRun: countOf("never-run"),
|
|
177
|
+
running: countOf("running"),
|
|
178
|
+
completed: countOf("completed"),
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
// The single next move for the CEO — the highest-priority report, or null
|
|
182
|
+
// when the fleet is healthy. This is the causation "next action".
|
|
183
|
+
let attention = null;
|
|
184
|
+
for (const state of ATTENTION_PRIORITY) {
|
|
185
|
+
const hit = reports.find((r) => r.state === state);
|
|
186
|
+
if (hit) { attention = hit; break; }
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Governance rollup is a pure read over the receipt stream — blocked
|
|
190
|
+
// mutation/execution attempts the CEO should be aware of. Zeroed when no
|
|
191
|
+
// receipts were supplied (the cockpit still renders the fleet).
|
|
192
|
+
const blockedAttempts = safeReceipts.filter(
|
|
193
|
+
(r) => r && r.outcomeStatus === "blocked"
|
|
194
|
+
).length;
|
|
195
|
+
|
|
196
|
+
return {
|
|
197
|
+
title: "CEO Cockpit",
|
|
198
|
+
fleet,
|
|
199
|
+
attention,
|
|
200
|
+
reports,
|
|
201
|
+
governance: { blockedAttempts },
|
|
202
|
+
generatedFromReceipts: safeReceipts.length > 0,
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
export default deriveCeoCockpit;
|
|
@@ -241,7 +241,9 @@ function checkSandboxRow(row, currentRow, path, violations) {
|
|
|
241
241
|
|
|
242
242
|
const incomingStatus = String(row.lifecycleStatus ?? "").trim().toLowerCase();
|
|
243
243
|
const currentStatus = String(currentRow?.lifecycleStatus ?? "").trim().toLowerCase();
|
|
244
|
+
const isHelperSetupRow = rowName(row) === "workspace-helper";
|
|
244
245
|
if (incomingStatus === "live" && currentStatus !== "live") {
|
|
246
|
+
if (isHelperSetupRow) return;
|
|
245
247
|
violations.push(violation(
|
|
246
248
|
"live_publish_via_patch",
|
|
247
249
|
`${path}.lifecycleStatus`,
|
|
@@ -298,6 +298,75 @@ const DASHBOARD_TEMPLATES = [
|
|
|
298
298
|
dashboard: { name: "Blank", status: "draft" },
|
|
299
299
|
widgets: []
|
|
300
300
|
},
|
|
301
|
+
{
|
|
302
|
+
id: "ceo-daily-operating",
|
|
303
|
+
name: "CEO Daily Operating Dashboard",
|
|
304
|
+
description: "An executive operating surface for the CEO loop: today's focus, the loop scorecard, direct reports, blocked work, reusable Agent Team blueprints, and governance receipts. Companion to the CEO Cockpit (/ceo) — Agent Teams are the atomic config layer, the Fleet is the runtime, and the live next move lives in the cockpit.",
|
|
305
|
+
category: "reporting",
|
|
306
|
+
bestFor: ["Founders", "Operators", "Workspace orchestrators"],
|
|
307
|
+
tags: ["ceo", "operating", "executive", "daily", "agent-teams"],
|
|
308
|
+
preview: {
|
|
309
|
+
layout: "operating-grid",
|
|
310
|
+
summary: "Today's focus, loop scorecard, direct reports, blocked work, Agent Team blueprints, governance receipts, and ritual notes"
|
|
311
|
+
},
|
|
312
|
+
dashboard: { name: "CEO Daily Operating Dashboard", status: "draft" },
|
|
313
|
+
widgets: [
|
|
314
|
+
createWidget("rich-text", "Today's CEO Focus", { x: 0, y: 0, w: 8, h: 4 }, {
|
|
315
|
+
text: "Set this each morning: the one highest-leverage move today, what is blocked, what needs review, and what to launch next. The live, computed next move is always in the CEO Cockpit — open it with /ceo.",
|
|
316
|
+
binding: { mode: "manual", source: "Manual text", rows: [] }
|
|
317
|
+
}),
|
|
318
|
+
createWidget("chart", "CEO Loop Scorecard", { x: 8, y: 0, w: 4, h: 4 }, {
|
|
319
|
+
values: [5, 4, 3, 3, 1, 3],
|
|
320
|
+
binding: { mode: "manual", source: "Sample — Created · Ready · Launched · Completed · Blocked · Reviewed", rows: [] }
|
|
321
|
+
}),
|
|
322
|
+
createWidget("view", "Direct Reports / Swarm Fleet", { x: 0, y: 4, w: 7, h: 5 }, {
|
|
323
|
+
source: "Sample",
|
|
324
|
+
layout: "Table",
|
|
325
|
+
columns: ["Workflow", "State", "Readiness", "Last Outcome", "Next Move"],
|
|
326
|
+
rows: [
|
|
327
|
+
{ Workflow: "Example research swarm", State: "Completed", Readiness: "Ready", "Last Outcome": "Success", "Next Move": "Review output" },
|
|
328
|
+
{ Workflow: "Example outreach swarm", State: "Not run yet", Readiness: "Ready", "Last Outcome": "—", "Next Move": "Launch" },
|
|
329
|
+
{ Workflow: "Example audit swarm", State: "Blocked", Readiness: "Needs target", "Last Outcome": "—", "Next Move": "Set execution target" }
|
|
330
|
+
],
|
|
331
|
+
binding: { mode: "manual", source: "Sample rows — the live fleet is in the CEO Cockpit (/ceo)", rows: [] }
|
|
332
|
+
}),
|
|
333
|
+
createWidget("view", "Blocked or Failing Work", { x: 7, y: 4, w: 5, h: 5 }, {
|
|
334
|
+
source: "Sample",
|
|
335
|
+
layout: "Table",
|
|
336
|
+
columns: ["Workflow", "Blocker", "Severity", "Next Action"],
|
|
337
|
+
rows: [
|
|
338
|
+
{ Workflow: "Example audit swarm", Blocker: "No execution target", Severity: "High", "Next Action": "Set local adapter" },
|
|
339
|
+
{ Workflow: "Example billing swarm", Blocker: "Last run failed", Severity: "High", "Next Action": "Review + re-run" }
|
|
340
|
+
],
|
|
341
|
+
binding: { mode: "manual", source: "Sample rows — live blockers are in the CEO Cockpit (/ceo)", rows: [] }
|
|
342
|
+
}),
|
|
343
|
+
createWidget("view", "Agent Team Blueprints", { x: 0, y: 9, w: 6, h: 4 }, {
|
|
344
|
+
source: "Sample",
|
|
345
|
+
layout: "Table",
|
|
346
|
+
columns: ["Team", "Orchestrator", "Sub-agents", "Outcome"],
|
|
347
|
+
rows: [
|
|
348
|
+
{ Team: "Research & Synthesis", Orchestrator: "Research Lead", "Sub-agents": "Researcher; Analyst; Synthesizer", Outcome: "Cited brief + recommendation" },
|
|
349
|
+
{ Team: "Outreach", Orchestrator: "Campaign Lead", "Sub-agents": "Writer; Reviewer", Outcome: "Approved sequence" }
|
|
350
|
+
],
|
|
351
|
+
binding: { mode: "manual", source: "Sample blueprints — manage real teams in the Agent Swarm Teams object and launch via /ceo (config layer, not runtime)", rows: [] }
|
|
352
|
+
}),
|
|
353
|
+
createWidget("view", "Governance Receipts / Decisions", { x: 6, y: 9, w: 6, h: 4 }, {
|
|
354
|
+
source: "Sample",
|
|
355
|
+
layout: "Table",
|
|
356
|
+
columns: ["Time", "Lane", "Action", "Outcome", "Receipt"],
|
|
357
|
+
rows: [
|
|
358
|
+
{ Time: "09:14", Lane: "execution-proof", Action: "sandbox-run", Outcome: "completed", Receipt: "rcpt_sample_1" },
|
|
359
|
+
{ Time: "09:31", Lane: "governed-proposal", Action: "helper/apply", Outcome: "applied", Receipt: "rcpt_sample_2" },
|
|
360
|
+
{ Time: "09:42", Lane: "untrusted-direct", Action: "PATCH", Outcome: "blocked", Receipt: "rcpt_sample_3" }
|
|
361
|
+
],
|
|
362
|
+
binding: { mode: "manual", source: "Sample rows — live stream: GET /api/workspace/agent-outcomes", rows: [] }
|
|
363
|
+
}),
|
|
364
|
+
createWidget("rich-text", "Daily Ritual Notes", { x: 0, y: 13, w: 12, h: 3 }, {
|
|
365
|
+
text: "Morning: set today's focus. Midday: launch or review. Afternoon: unblock the failing one first. End of day: confirm the receipts. Define reusable Agent Teams, then launch them as governed swarms — runs land in the Fleet. Open /ceo for the live next move.",
|
|
366
|
+
binding: { mode: "manual", source: "Manual text", rows: [] }
|
|
367
|
+
})
|
|
368
|
+
]
|
|
369
|
+
},
|
|
301
370
|
{
|
|
302
371
|
id: "client-portal",
|
|
303
372
|
name: "Client Portal",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@growthub/cli",
|
|
3
|
-
"version": "0.14.
|
|
3
|
+
"version": "0.14.4",
|
|
4
4
|
"description": "CLI control plane for Growthub Local and Agent Workspace as Code: export, fork, inspect, operate, sync, and optionally activate governed AI workspaces.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|