@growthub/cli 0.14.3 → 0.14.5
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/resolvers/[integrationId]/route.js +157 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/env-status/route.js +5 -1
- 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/api/workspace/resolvers/route.js +86 -4
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/ApiRegistryCreationCockpit.jsx +30 -5
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/ApiRegistryReviewModal.jsx +2 -2
- 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/DataModelShell.jsx +400 -188
- 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/OrchestrationGraphEmptyCanvas.jsx +1 -1
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/OrchestrationRunTracePanel.jsx +1 -1
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/SandboxOrchestrationEditorPanel.jsx +1 -1
- 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 +14 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/api-registry-creation-flow.js +24 -19
- 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/orchestration-graph.js +7 -82
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/resolver-constructor.js +217 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/server-resolver-registry.js +99 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/unified-resolver-registry.js +545 -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-resolver-proposal.js +30 -2
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-schema.js +69 -0
- package/package.json +2 -2
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/ApiRegistryActionCard.jsx +0 -141
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/SandboxToolConfirmModal.jsx +0 -64
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/SandboxToolDraftPanel.jsx +0 -376
|
@@ -48,6 +48,7 @@ import {
|
|
|
48
48
|
WorkspaceHelperSetupModal,
|
|
49
49
|
} from "../../components/WorkspaceHelperSetupModal.jsx";
|
|
50
50
|
import { SwarmRunCockpit, SwarmAgentTranscript } from "./SwarmRunCockpit.jsx";
|
|
51
|
+
import { CeoCockpit } from "./CeoCockpit.jsx";
|
|
51
52
|
import { SidecarExpandView } from "./SidecarExpandView.jsx";
|
|
52
53
|
import { parseSlashInput } from "./helper-commands.js";
|
|
53
54
|
import {
|
|
@@ -975,6 +976,10 @@ export function HelperSidecar({ open, onClose, workspaceConfig, initialIntent, i
|
|
|
975
976
|
};
|
|
976
977
|
|
|
977
978
|
const inSwarmView = activeView === "swarm-list" || activeView === "swarm-detail" || activeView === "tool-output";
|
|
979
|
+
// CEO cockpit shares the same sidecar shell as the swarm views (read-only
|
|
980
|
+
// oversight surface). Kept separate from inSwarmView so the swarm cockpit's
|
|
981
|
+
// run machinery and header affordances are untouched.
|
|
982
|
+
const inCeoView = activeView === "ceo";
|
|
978
983
|
const canOpenSwarmWorkflow = Boolean(
|
|
979
984
|
inSwarmView
|
|
980
985
|
&& activeTab === "assistant"
|
|
@@ -1009,7 +1014,7 @@ export function HelperSidecar({ open, onClose, workspaceConfig, initialIntent, i
|
|
|
1009
1014
|
{/* Header — title left; gear toggles Assistant ↔ Setup, then close. */}
|
|
1010
1015
|
<div className="dm-sidecar-header">
|
|
1011
1016
|
<div className="dm-sidecar-header-left">
|
|
1012
|
-
{inSwarmView && (
|
|
1017
|
+
{(inSwarmView || inCeoView) && (
|
|
1013
1018
|
<button
|
|
1014
1019
|
type="button"
|
|
1015
1020
|
className="dm-sidecar-icon-btn"
|
|
@@ -1028,9 +1033,11 @@ export function HelperSidecar({ open, onClose, workspaceConfig, initialIntent, i
|
|
|
1028
1033
|
<span className="dm-sidecar-title" data-helper-title="">
|
|
1029
1034
|
{inSwarmView
|
|
1030
1035
|
? "Background tasks"
|
|
1031
|
-
:
|
|
1032
|
-
?
|
|
1033
|
-
:
|
|
1036
|
+
: inCeoView
|
|
1037
|
+
? "CEO Cockpit"
|
|
1038
|
+
: threadActive
|
|
1039
|
+
? deriveThreadDisplayTitle(initialThread, "Workspace Helper")
|
|
1040
|
+
: "Workspace Helper"}
|
|
1034
1041
|
</span>
|
|
1035
1042
|
</div>
|
|
1036
1043
|
<div className="dm-sidecar-header-right">
|
|
@@ -1086,11 +1093,35 @@ export function HelperSidecar({ open, onClose, workspaceConfig, initialIntent, i
|
|
|
1086
1093
|
</div>
|
|
1087
1094
|
)}
|
|
1088
1095
|
|
|
1096
|
+
{/* CEO cockpit view — the chief-orchestrator oversight surface. Same
|
|
1097
|
+
sidecar shell, read-only: it derives the swarm fleet and hands every
|
|
1098
|
+
"Open" back to the swarm-detail surface (handleOpenArtifact) — no new
|
|
1099
|
+
route, no execution, no config mutation. */}
|
|
1100
|
+
{activeTab === "assistant" && inCeoView && (
|
|
1101
|
+
<div className="dm-sidecar-body dm-swarm-body" data-ceo-view={activeView}>
|
|
1102
|
+
<CeoCockpit
|
|
1103
|
+
workspaceConfig={workspaceConfig}
|
|
1104
|
+
onOpenArtifact={(artifact) => { if (artifact) handleOpenArtifact(artifact); }}
|
|
1105
|
+
onConfigRefresh={refreshWorkspaceConfig}
|
|
1106
|
+
onSeedSwarm={(seedPrompt) => {
|
|
1107
|
+
setActiveView("chat");
|
|
1108
|
+
onPickIntent("swarm");
|
|
1109
|
+
setPrompt(
|
|
1110
|
+
typeof seedPrompt === "string" && seedPrompt.trim()
|
|
1111
|
+
? `${seedPrompt.trim()} `
|
|
1112
|
+
: "Propose a governed agent swarm: "
|
|
1113
|
+
);
|
|
1114
|
+
}}
|
|
1115
|
+
onOpenSetup={() => setActiveTab("setup")}
|
|
1116
|
+
/>
|
|
1117
|
+
</div>
|
|
1118
|
+
)}
|
|
1119
|
+
|
|
1089
1120
|
{/* Assistant tab — composer-at-bottom layout (Twenty Ask AI parity):
|
|
1090
1121
|
conversation/result area on top (flex:1), bottom-anchored composer
|
|
1091
1122
|
holds chip stack (empty state) → mode row (active thread) →
|
|
1092
1123
|
textarea with attach + mode + send-arrow action row. */}
|
|
1093
|
-
{activeTab === "assistant" && !inSwarmView && (
|
|
1124
|
+
{activeTab === "assistant" && !inSwarmView && !inCeoView && (
|
|
1094
1125
|
<div className="dm-sidecar-body dm-helper-body">
|
|
1095
1126
|
<div className="dm-helper-conversation" ref={conversationRef}>
|
|
1096
1127
|
{/* Conversation — ChatGPT-grade multi-turn. User bubble
|
|
@@ -16,7 +16,7 @@ export function OrchestrationGraphEmptyCanvas({
|
|
|
16
16
|
<div className="dm-orchestration-canvas dm-orchestration-canvas--empty-state" aria-label="Empty orchestration graph">
|
|
17
17
|
<div className="dm-orchestration-canvas__empty-card">
|
|
18
18
|
<h3>Start orchestration graph</h3>
|
|
19
|
-
<p>Create a governed
|
|
19
|
+
<p>Create a governed workflow plan. Nothing executes until you run the workflow.</p>
|
|
20
20
|
<div className="dm-orchestration-canvas__empty-actions">
|
|
21
21
|
<button type="button" className="dm-btn-primary-sm" disabled={disabled} onClick={onStartFromRegistry}>
|
|
22
22
|
Start from API Registry
|
|
@@ -531,7 +531,7 @@ export function OrchestrationRunTracePanel({
|
|
|
531
531
|
Runs <span aria-hidden="true">/</span> <code>{activeConsoleRecord?.runId || "preview"}</code>
|
|
532
532
|
</span>
|
|
533
533
|
<h2>Run console</h2>
|
|
534
|
-
<p>{summaryText} · {row?.Name || "
|
|
534
|
+
<p>{summaryText} · {row?.Name || "Workflow"}</p>
|
|
535
535
|
</div>
|
|
536
536
|
<div className="dm-run-console__head-actions">
|
|
537
537
|
{canReplay && (
|
|
@@ -131,7 +131,7 @@ export function SandboxOrchestrationEditorPanel({
|
|
|
131
131
|
</button>
|
|
132
132
|
<div className="dm-orchestration-header__titles">
|
|
133
133
|
<h2>Orchestration graph</h2>
|
|
134
|
-
<p>{sandboxRow?.Name || "
|
|
134
|
+
<p>{sandboxRow?.Name || "Workflow"}</p>
|
|
135
135
|
</div>
|
|
136
136
|
</header>
|
|
137
137
|
|
|
@@ -43,12 +43,20 @@ export const HELPER_COMMANDS = [
|
|
|
43
43
|
{
|
|
44
44
|
name: "/swarm",
|
|
45
45
|
label: "Swarm",
|
|
46
|
-
description: "Propose a governed agent swarm —
|
|
46
|
+
description: "Propose a governed agent swarm — review and apply before any run (or start from an Agent Team blueprint in /ceo)",
|
|
47
47
|
scope: "swarm",
|
|
48
48
|
mutates: true,
|
|
49
49
|
intent: "swarm",
|
|
50
50
|
promptTemplate: "Propose a governed agent swarm:"
|
|
51
51
|
},
|
|
52
|
+
{
|
|
53
|
+
name: "/ceo",
|
|
54
|
+
label: "CEO",
|
|
55
|
+
description: "Open the CEO cockpit — fleet oversight, reusable Agent Teams, and your next move (read-only)",
|
|
56
|
+
scope: "workspace",
|
|
57
|
+
mutates: false,
|
|
58
|
+
view: "ceo"
|
|
59
|
+
},
|
|
52
60
|
{
|
|
53
61
|
name: "/register-api",
|
|
54
62
|
label: "Register API",
|
package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/globals.css
CHANGED
|
@@ -5046,6 +5046,9 @@ body.workspace-rail-collapsed .workspace-builder.dm-workflow-page {
|
|
|
5046
5046
|
}
|
|
5047
5047
|
.dm-api-action-card-success { grid-template-columns: 1fr auto; }
|
|
5048
5048
|
.dm-api-action-card-muted { grid-template-columns: 1fr; background: #fafafa; }
|
|
5049
|
+
.dm-api-action-card-workflow { grid-template-columns: 1fr; }
|
|
5050
|
+
.dm-api-action-card-workflow .dm-api-action-card-actions { align-items: flex-start; }
|
|
5051
|
+
.dm-api-action-card-workflow .dm-api-action-card-cta { align-self: flex-start; }
|
|
5049
5052
|
.dm-api-action-card-note { font-size: 11px; color: #6b7280; margin-top: 4px; }
|
|
5050
5053
|
.dm-api-action-checklist { margin: 8px 0 0; padding: 0; list-style: none; display: grid; gap: 6px; }
|
|
5051
5054
|
.dm-api-action-checklist li { display: flex; align-items: center; gap: 8px; font-size: 12px; color: #374151; }
|
|
@@ -9211,6 +9214,14 @@ body.workspace-rail-collapsed .workspace-builder.workspace-lens-page,
|
|
|
9211
9214
|
.dm-swarm-body { display: flex; flex-direction: column; min-height: 0; }
|
|
9212
9215
|
.dm-swarm-cockpit { flex: 1; min-height: 0; overflow-y: auto; padding: 12px 14px 16px; display: flex; flex-direction: column; gap: 10px; }
|
|
9213
9216
|
.dm-swarm-cockpit-list { display: flex; flex-direction: column; gap: 8px; }
|
|
9217
|
+
.dm-ceo-tabs { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 4px; margin: -4px 0 2px; }
|
|
9218
|
+
.dm-ceo-tabs button { min-height: 30px; padding: 5px 10px; border: 1px solid #e5e7eb; border-radius: 5px; background: #fff; color: #374151; font-size: 12px; font-weight: 600; cursor: pointer; }
|
|
9219
|
+
.dm-ceo-tabs button:hover { border-color: #cbd5e1; color: #111827; }
|
|
9220
|
+
.dm-ceo-tabs button.is-active { background: #111827; border-color: #111827; color: #fff; }
|
|
9221
|
+
/* CEO fleet list — layout-only grouping; the parent .dm-swarm-cockpit owns the
|
|
9222
|
+
scroll, so this never introduces a second scrollbar. Visible count is capped
|
|
9223
|
+
in the component (CEO_FLEET_VISIBLE_CAP). */
|
|
9224
|
+
.dm-ceo-report-list { display: grid; gap: 8px; }
|
|
9214
9225
|
.dm-swarm-section-row { display: flex; align-items: center; justify-content: space-between; margin-top: 4px; }
|
|
9215
9226
|
|
|
9216
9227
|
/* Run card — surface chrome comes from dm-helper-toolcall in the JSX. */
|
|
@@ -9218,6 +9229,9 @@ body.workspace-rail-collapsed .workspace-builder.workspace-lens-page,
|
|
|
9218
9229
|
.dm-swarm-card-head { display: flex; align-items: center; gap: 8px; }
|
|
9219
9230
|
.dm-swarm-card-title { flex: 1; }
|
|
9220
9231
|
.dm-swarm-card-action { height: 24px; padding: 0 7px; }
|
|
9232
|
+
.dm-ceo-card-redirect { opacity: 0; transition: opacity .12s ease, border-color .12s ease, color .12s ease; }
|
|
9233
|
+
.dm-swarm-card:hover .dm-ceo-card-redirect,
|
|
9234
|
+
.dm-swarm-card:focus-within .dm-ceo-card-redirect { opacity: 1; }
|
|
9221
9235
|
.dm-swarm-card-meta { display: flex; align-items: center; gap: 10px; }
|
|
9222
9236
|
.dm-swarm-card-meta .dm-run-console__hint { font-size: 12px; }
|
|
9223
9237
|
.dm-swarm-card-kind { font-weight: 600; }
|
|
@@ -6,13 +6,13 @@
|
|
|
6
6
|
* row being edited, the source-records sidecar, and the safe runtime signal, it
|
|
7
7
|
* resolves the full operator journey for THIS API as an ordered list of steps:
|
|
8
8
|
*
|
|
9
|
-
* register → configure auth → test → (resolver) →
|
|
10
|
-
*
|
|
9
|
+
* register → configure auth → test → (resolver) → data source → refresh
|
|
10
|
+
* records, then a separate workflow canvas CTA after activation.
|
|
11
11
|
*
|
|
12
12
|
* Each step carries a status (complete | active | pending | blocked | optional),
|
|
13
13
|
* a human description, and — when the operator can act — an `action` descriptor
|
|
14
14
|
* the drawer maps to an existing handler (test / create-data-source /
|
|
15
|
-
*
|
|
15
|
+
* open-data-source / refresh-source / create-workflow-canvas). The cockpit renders
|
|
16
16
|
* this verbatim, so the journey is one derivation, not UI guesswork.
|
|
17
17
|
*
|
|
18
18
|
* Invariants:
|
|
@@ -78,14 +78,18 @@ function sandboxRowsForIntegration(workspaceConfig, integrationId) {
|
|
|
78
78
|
const rows = [];
|
|
79
79
|
for (const object of findObjectsByType(workspaceConfig, "sandbox-environment")) {
|
|
80
80
|
for (const row of Array.isArray(object.rows) ? object.rows : []) {
|
|
81
|
-
const envRefs = clean(row?.envRefs);
|
|
82
81
|
const schedulerId = clean(row?.schedulerRegistryId);
|
|
83
|
-
const cfg = parseMaybeJson(
|
|
82
|
+
const cfg = parseMaybeJson(
|
|
83
|
+
row?.orchestrationConfig
|
|
84
|
+
|| row?.orchestrationGraph
|
|
85
|
+
|| row?.orchestrationDraftConfig
|
|
86
|
+
|| row?.orchestrationDraftGraph
|
|
87
|
+
);
|
|
84
88
|
const callsApi = Array.isArray(cfg?.nodes) && cfg.nodes.some(
|
|
85
89
|
(n) => n?.type === "api-registry-call"
|
|
86
90
|
&& clean(n?.config?.registryId || n?.config?.integrationId) === id,
|
|
87
91
|
);
|
|
88
|
-
if (callsApi || schedulerId === id
|
|
92
|
+
if (callsApi || schedulerId === id) {
|
|
89
93
|
rows.push(row);
|
|
90
94
|
}
|
|
91
95
|
}
|
|
@@ -213,8 +217,11 @@ function deriveApiRegistryCreationState(input = {}) {
|
|
|
213
217
|
status: resolverWired ? "complete" : (tested ? "optional" : "blocked"),
|
|
214
218
|
description: resolverWired
|
|
215
219
|
? `Resolver "${resolverTemplate}" shapes the response into rows.`
|
|
216
|
-
: "Optional:
|
|
217
|
-
|
|
220
|
+
: "Optional: construct a resolver to normalize the response into governed rows. Raw passthrough works without one.",
|
|
221
|
+
// CMS SDK v1.5.1 — the action constructs the governed resolver from the
|
|
222
|
+
// tested response shape (no blank form). The cockpit stages it for one-screen
|
|
223
|
+
// review, applies through the governed lane, and re-tests.
|
|
224
|
+
action: tested && !resolverWired ? { id: "construct-resolver", label: "Construct resolver" } : null,
|
|
218
225
|
});
|
|
219
226
|
|
|
220
227
|
step({
|
|
@@ -245,17 +252,6 @@ function deriveApiRegistryCreationState(input = {}) {
|
|
|
245
252
|
: null,
|
|
246
253
|
});
|
|
247
254
|
|
|
248
|
-
// Optional automation lane — a sandbox/workflow that calls this API.
|
|
249
|
-
step({
|
|
250
|
-
id: "sandbox-tool",
|
|
251
|
-
label: "Automate (sandbox tool)",
|
|
252
|
-
status: sandboxExists ? "complete" : (tested ? "optional" : "blocked"),
|
|
253
|
-
description: sandboxExists
|
|
254
|
-
? "A sandbox tool calls this API."
|
|
255
|
-
: "Optional: wrap this API in a sandbox/workflow you can run or schedule.",
|
|
256
|
-
action: tested && !sandboxExists ? { id: "create-sandbox-tool", label: "Create sandbox tool" } : null,
|
|
257
|
-
});
|
|
258
|
-
|
|
259
255
|
for (const s of steps) { if (!s.hint) delete s.hint; }
|
|
260
256
|
|
|
261
257
|
const required = steps.filter((s) => s.status !== "optional");
|
|
@@ -297,6 +293,15 @@ function deriveApiRegistryCreationState(input = {}) {
|
|
|
297
293
|
score,
|
|
298
294
|
nextStepId: nextStep ? nextStep.id : null,
|
|
299
295
|
nextAction: nextStep && nextStep.action ? { stepId: nextStep.id, ...nextStep.action } : null,
|
|
296
|
+
workflowAction: tested
|
|
297
|
+
? {
|
|
298
|
+
id: sandboxExists ? "open-workflow-canvas" : "create-workflow-canvas",
|
|
299
|
+
label: sandboxExists ? "Open workflow canvas" : "Create workflow",
|
|
300
|
+
description: sandboxExists
|
|
301
|
+
? "Open the workflow canvas that uses this API Registry node."
|
|
302
|
+
: "Open a workflow canvas with this API Registry call already drafted.",
|
|
303
|
+
}
|
|
304
|
+
: null,
|
|
300
305
|
headline: !registered
|
|
301
306
|
? "Register this API to begin."
|
|
302
307
|
: complete
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CEO Agent Teams — the atomic, reusable team-configuration layer for the /ceo
|
|
3
|
+
* surface (CEO_PRIMITIVE_COCKPIT_ROADMAP_V1).
|
|
4
|
+
*
|
|
5
|
+
* Distinction this module encodes:
|
|
6
|
+
* - FLEET (runtime/oversight) = existing `swarm-workflows` rows, readiness,
|
|
7
|
+
* run state, failures, receipts, Background Tasks. UNCHANGED by this module.
|
|
8
|
+
* - AGENT TEAMS (atomic configuration) = reusable blueprints describing the
|
|
9
|
+
* orchestrator, sub-agent roles, skills, processes, workflow
|
|
10
|
+
* responsibilities, and outcome criteria of a swarm.
|
|
11
|
+
*
|
|
12
|
+
* This is a PURE module — no React, no fetch, no fs, no writes, no localStorage.
|
|
13
|
+
* It introduces NO new runtime, executor, API route, PATCH allowlist field, or
|
|
14
|
+
* object type: Agent Teams live in a governed Data Model object of the EXISTING
|
|
15
|
+
* `custom` objectType, created through the EXISTING `dataModel.object.create`
|
|
16
|
+
* helper/apply lane, and they only ever *inform* a `/swarm` proposal — the
|
|
17
|
+
* server still builds the `agent-swarm-v1` graph, the run still lands in
|
|
18
|
+
* `swarm-workflows`, and execution still happens through `sandbox-run`.
|
|
19
|
+
*
|
|
20
|
+
* An Agent Team record never executes anything.
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
export const AGENT_SWARM_TEAMS_OBJECT_ID = "agent-swarm-teams";
|
|
24
|
+
export const AGENT_SWARM_TEAMS_LABEL = "Agent Swarm Teams";
|
|
25
|
+
|
|
26
|
+
// Visible row columns. "Name" is the capital-N identity column (Data Model grid
|
|
27
|
+
// convention). Array-shaped fields are stored as readable strings so we never
|
|
28
|
+
// fight the row-value contract (no typed arrays in rows).
|
|
29
|
+
export const AGENT_SWARM_TEAMS_COLUMNS = [
|
|
30
|
+
"id",
|
|
31
|
+
"Name",
|
|
32
|
+
"status",
|
|
33
|
+
"teamPurpose",
|
|
34
|
+
"orchestratorRole",
|
|
35
|
+
"orchestratorPrompt",
|
|
36
|
+
"subAgentRoles",
|
|
37
|
+
"skills",
|
|
38
|
+
"processes",
|
|
39
|
+
"workflowResponsibilities",
|
|
40
|
+
"outcomeCriteria",
|
|
41
|
+
"defaultRunLocality",
|
|
42
|
+
"defaultAdapter",
|
|
43
|
+
"linkedSwarmWorkflowName",
|
|
44
|
+
"governanceNotes",
|
|
45
|
+
"createdAt",
|
|
46
|
+
"updatedAt",
|
|
47
|
+
];
|
|
48
|
+
|
|
49
|
+
function clean(value) {
|
|
50
|
+
return typeof value === "string" ? value.trim() : "";
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function slugify(value) {
|
|
54
|
+
return clean(value).toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// One clearly-labeled blueprint row so a freshly-created table shows the shape
|
|
58
|
+
// (and is honestly an example, not live state).
|
|
59
|
+
function blueprintRow() {
|
|
60
|
+
return {
|
|
61
|
+
id: "team-research-synthesis",
|
|
62
|
+
Name: "Example — Research & Synthesis Team",
|
|
63
|
+
status: "blueprint",
|
|
64
|
+
teamPurpose: "Reusable blueprint: research a topic and synthesize a cited brief.",
|
|
65
|
+
orchestratorRole: "Research Lead",
|
|
66
|
+
orchestratorPrompt: "Decompose the objective into independent research subtasks for the team.",
|
|
67
|
+
subAgentRoles: "Researcher; Analyst; Synthesizer",
|
|
68
|
+
skills: "web-research; summarization; critique",
|
|
69
|
+
processes: "gather → analyze → synthesize",
|
|
70
|
+
workflowResponsibilities: "Researcher gathers facts; Analyst stress-tests; Synthesizer writes the brief",
|
|
71
|
+
outcomeCriteria: "A cited brief with risks and a clear recommendation",
|
|
72
|
+
defaultRunLocality: "local",
|
|
73
|
+
defaultAdapter: "local-intelligence",
|
|
74
|
+
linkedSwarmWorkflowName: "",
|
|
75
|
+
governanceNotes: "Blueprint only — launch through /swarm; the run lands in swarm-workflows and emits receipts.",
|
|
76
|
+
createdAt: "",
|
|
77
|
+
updatedAt: "",
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** The governed Agent Teams object definition (existing `custom` objectType). */
|
|
82
|
+
export function buildAgentSwarmTeamsObject({ includeBlueprint = true } = {}) {
|
|
83
|
+
return {
|
|
84
|
+
id: AGENT_SWARM_TEAMS_OBJECT_ID,
|
|
85
|
+
label: AGENT_SWARM_TEAMS_LABEL,
|
|
86
|
+
objectType: "custom",
|
|
87
|
+
columns: AGENT_SWARM_TEAMS_COLUMNS.slice(),
|
|
88
|
+
rows: includeBlueprint ? [blueprintRow()] : [],
|
|
89
|
+
binding: { mode: "manual", source: AGENT_SWARM_TEAMS_LABEL },
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* A governed proposal that CREATES the Agent Teams table through the EXISTING
|
|
95
|
+
* `dataModel.object.create` helper/apply lane. No new proposal type, no new
|
|
96
|
+
* objectType — objectType stays `custom`.
|
|
97
|
+
*/
|
|
98
|
+
export function buildCreateAgentTeamsProposal() {
|
|
99
|
+
return {
|
|
100
|
+
type: "dataModel.object.create",
|
|
101
|
+
affectedField: "dataModel",
|
|
102
|
+
payload: { object: buildAgentSwarmTeamsObject({ includeBlueprint: true }) },
|
|
103
|
+
rationale: "Create the governed Agent Swarm Teams table — reusable swarm blueprints (config, not runtime).",
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export function findAgentTeamsObject(workspaceConfig) {
|
|
108
|
+
const objects = Array.isArray(workspaceConfig?.dataModel?.objects) ? workspaceConfig.dataModel.objects : [];
|
|
109
|
+
return objects.find((o) => o?.id === AGENT_SWARM_TEAMS_OBJECT_ID) || null;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export function findAgentTeams(workspaceConfig) {
|
|
113
|
+
const object = findAgentTeamsObject(workspaceConfig);
|
|
114
|
+
const rows = Array.isArray(object?.rows) ? object.rows : [];
|
|
115
|
+
const seen = new Set();
|
|
116
|
+
return rows
|
|
117
|
+
.filter((row) => row && clean(row.Name))
|
|
118
|
+
.map((row, index) => {
|
|
119
|
+
const name = clean(row.Name);
|
|
120
|
+
// Stable atomic identity: explicit id, else a slug of Name, else index —
|
|
121
|
+
// de-duplicated so two same-named blueprints never collide as React keys
|
|
122
|
+
// (the same fix applied to fleet reports).
|
|
123
|
+
let teamId = clean(row.id) || slugify(name) || `team-${index + 1}`;
|
|
124
|
+
while (seen.has(teamId)) teamId = `${teamId}-${index + 1}`;
|
|
125
|
+
seen.add(teamId);
|
|
126
|
+
return {
|
|
127
|
+
teamId,
|
|
128
|
+
name,
|
|
129
|
+
status: clean(row.status) || "blueprint",
|
|
130
|
+
teamPurpose: clean(row.teamPurpose),
|
|
131
|
+
orchestratorRole: clean(row.orchestratorRole),
|
|
132
|
+
orchestratorPrompt: clean(row.orchestratorPrompt),
|
|
133
|
+
subAgentRoles: clean(row.subAgentRoles),
|
|
134
|
+
skills: clean(row.skills),
|
|
135
|
+
processes: clean(row.processes),
|
|
136
|
+
workflowResponsibilities: clean(row.workflowResponsibilities),
|
|
137
|
+
outcomeCriteria: clean(row.outcomeCriteria),
|
|
138
|
+
defaultRunLocality: clean(row.defaultRunLocality),
|
|
139
|
+
defaultAdapter: clean(row.defaultAdapter),
|
|
140
|
+
linkedSwarmWorkflowName: clean(row.linkedSwarmWorkflowName),
|
|
141
|
+
governanceNotes: clean(row.governanceNotes),
|
|
142
|
+
};
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Project the Agent Teams configuration layer for the cockpit. Pure.
|
|
148
|
+
* { present, count, teams[], canCreate }
|
|
149
|
+
*/
|
|
150
|
+
export function deriveAgentTeamsState({ workspaceConfig } = {}) {
|
|
151
|
+
const object = findAgentTeamsObject(workspaceConfig);
|
|
152
|
+
const teams = findAgentTeams(workspaceConfig);
|
|
153
|
+
return {
|
|
154
|
+
objectId: AGENT_SWARM_TEAMS_OBJECT_ID,
|
|
155
|
+
present: Boolean(object),
|
|
156
|
+
count: teams.length,
|
|
157
|
+
teams,
|
|
158
|
+
// The table is created through the governed create lane when absent.
|
|
159
|
+
canCreate: !object,
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function joinAgents(subAgentRoles) {
|
|
164
|
+
return clean(subAgentRoles)
|
|
165
|
+
.split(/[;,\n]/)
|
|
166
|
+
.map((part) => part.trim())
|
|
167
|
+
.filter(Boolean);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Build the /swarm composer seed text from an Agent Team blueprint. This is
|
|
172
|
+
* PROPOSE-ONLY seed copy: it prefills the helper composer so the user proposes
|
|
173
|
+
* a governed swarm "from this blueprint". The model + server still produce the
|
|
174
|
+
* proposal and build the agent-swarm-v1 graph; nothing here executes.
|
|
175
|
+
*/
|
|
176
|
+
export function buildSwarmIntentFromTeam(team) {
|
|
177
|
+
if (!team) return "Propose a governed agent swarm:";
|
|
178
|
+
const agents = joinAgents(team.subAgentRoles);
|
|
179
|
+
const target = [
|
|
180
|
+
team.defaultAdapter ? `adapter ${team.defaultAdapter}` : "",
|
|
181
|
+
team.defaultRunLocality ? `${team.defaultRunLocality} run target` : "",
|
|
182
|
+
].filter(Boolean).join(", ");
|
|
183
|
+
// Carry the full atomic configuration forward so the blueprint is a true
|
|
184
|
+
// configuration inversion of the swarm — still propose-only seed copy; the
|
|
185
|
+
// server builds the agent-swarm-v1 graph and nothing executes here.
|
|
186
|
+
const parts = [
|
|
187
|
+
`Propose a governed agent swarm from the Agent Team blueprint "${team.name}".`,
|
|
188
|
+
team.teamPurpose ? `Objective: ${team.teamPurpose}` : "",
|
|
189
|
+
team.orchestratorRole || team.orchestratorPrompt
|
|
190
|
+
? `Orchestrator${team.orchestratorRole ? ` (${team.orchestratorRole})` : ""}: ${team.orchestratorPrompt || "plan the work for the team"}.`
|
|
191
|
+
: "",
|
|
192
|
+
agents.length ? `Sub-agents: ${agents.join(", ")}.` : "",
|
|
193
|
+
team.skills ? `Skills: ${team.skills}.` : "",
|
|
194
|
+
team.processes ? `Process: ${team.processes}.` : "",
|
|
195
|
+
team.workflowResponsibilities ? `Workflow responsibilities: ${team.workflowResponsibilities}.` : "",
|
|
196
|
+
team.outcomeCriteria ? `Outcome criteria: ${team.outcomeCriteria}.` : "",
|
|
197
|
+
target ? `Preferred execution target: ${target}.` : "",
|
|
198
|
+
team.governanceNotes ? `Governance notes: ${team.governanceNotes}.` : "",
|
|
199
|
+
];
|
|
200
|
+
return parts.filter(Boolean).join(" ");
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
export function summarizeTeam(team) {
|
|
204
|
+
const agents = joinAgents(team?.subAgentRoles).length;
|
|
205
|
+
return [
|
|
206
|
+
team?.orchestratorRole ? `orchestrator: ${team.orchestratorRole}` : null,
|
|
207
|
+
agents ? `${agents} sub-agent${agents === 1 ? "" : "s"}` : null,
|
|
208
|
+
].filter(Boolean).join(" · ");
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
export default deriveAgentTeamsState;
|