@growthub/cli 0.14.1 → 0.14.3
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/SKILL.md +4 -2
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/agent-outcomes/route.js +85 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/apps/route.js +187 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/helper/apply/route.js +36 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/patch/preflight/route.js +152 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/refresh-sources/route.js +21 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/route.js +88 -1
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/sandbox-agent-auth/login/route.js +3 -2
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/sandbox-agent-auth/logout/route.js +3 -2
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/sandbox-agent-auth/status/route.js +3 -2
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/sandbox-run/route.js +86 -2
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/swarm-condition/route.js +2 -2
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/test-source/route.js +21 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/workflow/publish/route.js +338 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/components/WorkspaceHelperSetupModal.jsx +1 -1
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/components/WorkspaceLensPanel.jsx +1 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/AgentSwarmPanel.jsx +49 -2
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/DataModelShell.jsx +54 -11
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/OrchestrationNodeConfigPanel.jsx +113 -36
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/SandboxAgentAuthPanel.jsx +34 -14
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/globals.css +7 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/workflows/WorkflowSurface.jsx +35 -169
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/docs/sandbox-environment-primitive.md +26 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/sandboxes/adapters/local-intelligence-browser-access.js +516 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/sandboxes/default-local-agent-host.js +85 -7
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/sandboxes/default-local-process.js +3 -1
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/sandboxes/index.js +1 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/sandboxes/sandbox-adapter-registry.js +5 -1
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/data-model/field-contracts.js +1 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/orchestration-agent-swarm.js +8 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/orchestration-graph-runner.js +3 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/orchestration-graph.js +4 -2
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/orchestration-publish.js +179 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/orchestration-run-console.js +1 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/sandbox-agent-auth.js +82 -27
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/sandbox-serverless-flow.js +4 -2
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-activation.js +89 -5
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-app-registry.js +539 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-config.js +11 -2
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-data-model.js +24 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-outcome-receipts.js +157 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-patch-policy.js +400 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-schema.js +6 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-swarm-proposal.js +3 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/package-lock.json +364 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/package.json +1 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/kit.json +10 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/skills/governed-workspace-mutation/SKILL.md +203 -0
- package/package.json +2 -2
|
@@ -3,6 +3,17 @@
|
|
|
3
3
|
import { useMemo } from "react";
|
|
4
4
|
import { Check, Plus, Trash2 } from "lucide-react";
|
|
5
5
|
import { HOST_AUTH_CATALOG } from "@/lib/sandbox-agent-host-catalog";
|
|
6
|
+
import { SandboxAgentAuthPanel } from "./SandboxAgentAuthPanel.jsx";
|
|
7
|
+
import { isSandboxLocalAgentHost } from "@/lib/sandbox-agent-auth-eligibility";
|
|
8
|
+
|
|
9
|
+
const EMPTY_AGENT_AUTH_PATCH = {
|
|
10
|
+
agentAuthStatus: "",
|
|
11
|
+
agentAuthProvider: "",
|
|
12
|
+
agentAuthLastChecked: "",
|
|
13
|
+
agentAuthLastExitCode: "",
|
|
14
|
+
agentAuthLastMessage: "",
|
|
15
|
+
agentAuthLastLoginUrl: ""
|
|
16
|
+
};
|
|
6
17
|
|
|
7
18
|
function getHostOptions() {
|
|
8
19
|
return Object.entries(HOST_AUTH_CATALOG || {}).map(([slug, host]) => ({
|
|
@@ -26,6 +37,17 @@ function withRecordRef(patch, objectId, rowName, nodeId) {
|
|
|
26
37
|
};
|
|
27
38
|
}
|
|
28
39
|
|
|
40
|
+
function buildSubagentAuthDraft(sandboxRow, config) {
|
|
41
|
+
const agentHost = String(config?.agentHost || sandboxRow?.agentHost || "").trim();
|
|
42
|
+
if (!agentHost) return null;
|
|
43
|
+
return {
|
|
44
|
+
...(sandboxRow || {}),
|
|
45
|
+
runLocality: "local",
|
|
46
|
+
adapter: "local-agent-host",
|
|
47
|
+
agentHost
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
29
51
|
function WorkflowCheckbox({ checked, disabled, onChange, children, title }) {
|
|
30
52
|
return (
|
|
31
53
|
<label className="dm-orchestration-config__field dm-orchestration-config__field-inline dm-workflow-check" title={title}>
|
|
@@ -139,7 +161,7 @@ function patchSwarmConfig(graph, patch) {
|
|
|
139
161
|
return { ...graph, swarm: { ...base, ...patch } };
|
|
140
162
|
}
|
|
141
163
|
|
|
142
|
-
export function AgentSwarmPanel({ graph, objectId, rowName, onGraphChange, disabled }) {
|
|
164
|
+
export function AgentSwarmPanel({ graph, objectId, rowName, sandboxRow, onSandboxRowPatch, onGraphChange, disabled }) {
|
|
143
165
|
const hostOptions = useMemo(getHostOptions, []);
|
|
144
166
|
if (!graph || typeof graph !== "object") return null;
|
|
145
167
|
|
|
@@ -154,6 +176,21 @@ export function AgentSwarmPanel({ graph, objectId, rowName, onGraphChange, disab
|
|
|
154
176
|
onGraphChange?.(updater);
|
|
155
177
|
}
|
|
156
178
|
|
|
179
|
+
function patchSubagentHost(nodeId, agentHost) {
|
|
180
|
+
const nextHost = String(agentHost || "").trim();
|
|
181
|
+
patchGraph((g) => patchSubagent(g, nodeId, {
|
|
182
|
+
agentHost: nextHost,
|
|
183
|
+
...(nextHost ? { adapter: "local-agent-host" } : {})
|
|
184
|
+
}, objectId, rowName));
|
|
185
|
+
if (nextHost && typeof onSandboxRowPatch === "function") {
|
|
186
|
+
onSandboxRowPatch({
|
|
187
|
+
adapter: "local-agent-host",
|
|
188
|
+
agentHost: nextHost,
|
|
189
|
+
...EMPTY_AGENT_AUTH_PATCH
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
157
194
|
return (
|
|
158
195
|
<div className="dm-orchestration-config dm-agent-swarm-panel">
|
|
159
196
|
<div className="dm-orchestration-config__pane">
|
|
@@ -187,6 +224,7 @@ export function AgentSwarmPanel({ graph, objectId, rowName, onGraphChange, disab
|
|
|
187
224
|
)}
|
|
188
225
|
{subagents.map((node) => {
|
|
189
226
|
const cfg = node.config || {};
|
|
227
|
+
const subagentAuthDraft = buildSubagentAuthDraft(sandboxRow, cfg);
|
|
190
228
|
return (
|
|
191
229
|
<div key={node.id} className="dm-agent-swarm-panel__subagent">
|
|
192
230
|
<div className="dm-agent-swarm-panel__row">
|
|
@@ -252,7 +290,7 @@ export function AgentSwarmPanel({ graph, objectId, rowName, onGraphChange, disab
|
|
|
252
290
|
<select
|
|
253
291
|
value={cfg.agentHost || ""}
|
|
254
292
|
disabled={disabled}
|
|
255
|
-
onChange={(e) =>
|
|
293
|
+
onChange={(e) => patchSubagentHost(node.id, e.target.value)}
|
|
256
294
|
>
|
|
257
295
|
<option value="">Inherit</option>
|
|
258
296
|
{hostOptions.map((opt) => (
|
|
@@ -260,6 +298,15 @@ export function AgentSwarmPanel({ graph, objectId, rowName, onGraphChange, disab
|
|
|
260
298
|
))}
|
|
261
299
|
</select>
|
|
262
300
|
</label>
|
|
301
|
+
{subagentAuthDraft && isSandboxLocalAgentHost(subagentAuthDraft) && (
|
|
302
|
+
<SandboxAgentAuthPanel
|
|
303
|
+
objectId={objectId}
|
|
304
|
+
rowName={rowName}
|
|
305
|
+
draft={subagentAuthDraft}
|
|
306
|
+
disabled={disabled || typeof onSandboxRowPatch !== "function"}
|
|
307
|
+
onPatchDraft={onSandboxRowPatch}
|
|
308
|
+
/>
|
|
309
|
+
)}
|
|
263
310
|
<WorkflowCheckbox
|
|
264
311
|
checked={cfg.required !== false}
|
|
265
312
|
disabled={disabled}
|
|
@@ -647,6 +647,14 @@ function SandboxTraceFieldButton({ label, value, disabled, onOpen }) {
|
|
|
647
647
|
);
|
|
648
648
|
}
|
|
649
649
|
|
|
650
|
+
// Human labels for the per-host browser lanes declared in the
|
|
651
|
+
// local-agent-host catalog — surfaced so the operator's mental model matches
|
|
652
|
+
// exactly what the adapter does under the hood when browserAccess is on.
|
|
653
|
+
const BROWSER_LANE_LABELS = {
|
|
654
|
+
"native-flag": "browser enabled through the host CLI's first-party browser integration flags.",
|
|
655
|
+
"env-signal": "host receives GROWTHUB_SANDBOX_BROWSER_ACCESS=1 — its own configured browser integration honors this setting."
|
|
656
|
+
};
|
|
657
|
+
|
|
650
658
|
function SandboxRecordFields({
|
|
651
659
|
draft,
|
|
652
660
|
setDraft,
|
|
@@ -705,8 +713,21 @@ function SandboxRecordFields({
|
|
|
705
713
|
return { ...fields, ...EMPTY_AGENT_AUTH_PATCH };
|
|
706
714
|
}
|
|
707
715
|
|
|
716
|
+
function defaultSchedulerRegistryId() {
|
|
717
|
+
const objects = Array.isArray(workspaceConfig?.dataModel?.objects) ? workspaceConfig.dataModel.objects : [];
|
|
718
|
+
for (const object of objects) {
|
|
719
|
+
if (object?.objectType !== "api-registry") continue;
|
|
720
|
+
const row = (object.rows || []).find((r) => String(r?.integrationId || "").trim());
|
|
721
|
+
if (row) return String(row.integrationId || "").trim();
|
|
722
|
+
}
|
|
723
|
+
return "";
|
|
724
|
+
}
|
|
725
|
+
|
|
708
726
|
function setRunLocality(next) {
|
|
709
727
|
const fields = { runLocality: next };
|
|
728
|
+
if (next === "serverless") {
|
|
729
|
+
fields.schedulerRegistryId = String(draft.schedulerRegistryId || "").trim() || defaultSchedulerRegistryId();
|
|
730
|
+
}
|
|
710
731
|
if (next === "serverless" && ["local-agent-host", "local-intelligence"].includes(String(draft.adapter || "").trim())) {
|
|
711
732
|
fields.adapter = "local-process";
|
|
712
733
|
fields.agentHost = "";
|
|
@@ -724,6 +745,8 @@ function SandboxRecordFields({
|
|
|
724
745
|
}
|
|
725
746
|
|
|
726
747
|
const netOn = ["true", "1", "on", "yes"].includes(String(draft.networkAllow || "").trim().toLowerCase());
|
|
748
|
+
const browserOn = ["true", "1", "on", "yes"].includes(String(draft.browserAccess || "").trim().toLowerCase());
|
|
749
|
+
const browserHostMeta = (selectedAdapterMeta?.hostCatalog || []).find((h) => h.slug === String(draft.agentHost || "").trim());
|
|
727
750
|
|
|
728
751
|
// Same cockpit interface + mental model as the API Registry lane, driven by
|
|
729
752
|
// the serverless/scheduling/persistence derivation. Steps are status-only
|
|
@@ -735,6 +758,7 @@ function SandboxRecordFields({
|
|
|
735
758
|
persistenceAdapters: serverlessSignals.persistenceAdapters,
|
|
736
759
|
inlineEditing: true,
|
|
737
760
|
});
|
|
761
|
+
const showServerlessUpgrade = String(draft.adapter || "").trim() !== "local-intelligence";
|
|
738
762
|
function handleServerlessAction(action) {
|
|
739
763
|
if (!action) return;
|
|
740
764
|
if (action.id === "toggle-locality") setRunLocality(serverlessState.isServerless ? "local" : "serverless");
|
|
@@ -743,12 +767,14 @@ function SandboxRecordFields({
|
|
|
743
767
|
|
|
744
768
|
return (
|
|
745
769
|
<div className="dm-sandbox-config">
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
770
|
+
{showServerlessUpgrade && (
|
|
771
|
+
<ApiRegistryCreationCockpit
|
|
772
|
+
state={serverlessState}
|
|
773
|
+
onAction={handleServerlessAction}
|
|
774
|
+
disabled={!table.mutable || saving}
|
|
775
|
+
eyebrow={serverlessState.isServerless ? "Serverless workflow" : "Workflow runtime"}
|
|
776
|
+
/>
|
|
777
|
+
)}
|
|
752
778
|
<DrawerSection title="Identity & Mode">
|
|
753
779
|
<label className="dm-record-field">
|
|
754
780
|
<span>Name</span>
|
|
@@ -791,7 +817,7 @@ function SandboxRecordFields({
|
|
|
791
817
|
onChange={setRunLocality}
|
|
792
818
|
/>
|
|
793
819
|
<p className="dm-cell-empty" style={{ fontSize: 11, marginTop: 6 }}>
|
|
794
|
-
|
|
820
|
+
Choose local execution or a scheduled serverless run.
|
|
795
821
|
</p>
|
|
796
822
|
|
|
797
823
|
{locality === "serverless" && table.objectId && (
|
|
@@ -883,7 +909,7 @@ function SandboxRecordFields({
|
|
|
883
909
|
</label>
|
|
884
910
|
|
|
885
911
|
<p className="dm-cell-empty" style={{ fontSize: 11, marginTop: 0 }}>
|
|
886
|
-
Uses <strong>Instructions</strong> + <strong>Command</strong> as the task payload.
|
|
912
|
+
Uses <strong>Instructions</strong> + <strong>Command</strong> as the task payload. With browser access off, tool intents stay proposals. With browser access on, browser tool intents execute through the local browser bridge before the final JSON response is returned.
|
|
887
913
|
</p>
|
|
888
914
|
</div>
|
|
889
915
|
)}
|
|
@@ -921,10 +947,12 @@ function SandboxRecordFields({
|
|
|
921
947
|
</div>
|
|
922
948
|
|
|
923
949
|
<ToggleField
|
|
924
|
-
checked={netOn}
|
|
925
|
-
disabled={!table.mutable || saving}
|
|
950
|
+
checked={netOn || browserOn}
|
|
951
|
+
disabled={!table.mutable || saving || (browserOn && !netOn)}
|
|
926
952
|
label="Network allow-list mode"
|
|
927
|
-
description=
|
|
953
|
+
description={browserOn && !netOn
|
|
954
|
+
? "Network enabled by browser access — the run route grants it even though this row's networkAllow is off. Turn browser access off to control network independently."
|
|
955
|
+
: "When enabled, local runs honor GROWTHUB_SANDBOX_NET_* and the allow list below."}
|
|
928
956
|
onChange={(on) => patchFields({ networkAllow: on ? "true" : "false" })}
|
|
929
957
|
/>
|
|
930
958
|
|
|
@@ -937,6 +965,21 @@ function SandboxRecordFields({
|
|
|
937
965
|
onBlur={(event) => patchFields({ allowList: event.target.value })}
|
|
938
966
|
/>
|
|
939
967
|
</label>
|
|
968
|
+
|
|
969
|
+
<ToggleField
|
|
970
|
+
checked={browserOn}
|
|
971
|
+
disabled={!table.mutable || saving}
|
|
972
|
+
label="Browser access"
|
|
973
|
+
description="Allows this sandbox to use a real browser. Also enables network. Local intelligence uses the Playwright browser bridge; Codex/Claude use their native browser modes."
|
|
974
|
+
onChange={(on) => patchFields(on
|
|
975
|
+
? { browserAccess: "true", networkAllow: "true" }
|
|
976
|
+
: { browserAccess: "false" })}
|
|
977
|
+
/>
|
|
978
|
+
{browserOn && String(draft.adapter || "").trim() === "local-agent-host" && browserHostMeta && (
|
|
979
|
+
<p className="dm-cell-empty" style={{ fontSize: 11, marginTop: 4 }}>
|
|
980
|
+
{browserHostMeta.label}: {BROWSER_LANE_LABELS[browserHostMeta.browserLane] || BROWSER_LANE_LABELS["env-signal"]}
|
|
981
|
+
</p>
|
|
982
|
+
)}
|
|
940
983
|
</DrawerSection>
|
|
941
984
|
|
|
942
985
|
<DrawerSection title="Prompt & Limits">
|
|
@@ -20,6 +20,12 @@ const LOCAL_AGENT_ADAPTERS = [
|
|
|
20
20
|
{ value: "local-agent-host", label: "Local agent host" },
|
|
21
21
|
{ value: "local-intelligence", label: "Local intelligence" }
|
|
22
22
|
];
|
|
23
|
+
const LOCAL_INTELLIGENCE_MODE_OPTIONS = [
|
|
24
|
+
{ value: "ollama", label: "ollama (OLLAMA_BASE_URL + /v1/chat/completions)" },
|
|
25
|
+
{ value: "lmstudio", label: "lmstudio (LMSTUDIO_BASE_URL)" },
|
|
26
|
+
{ value: "vllm", label: "vllm (VLLM_BASE_URL required)" },
|
|
27
|
+
{ value: "custom-openai-compatible", label: "custom (use Chat completions URL above)" }
|
|
28
|
+
];
|
|
23
29
|
const EMPTY_AGENT_AUTH_PATCH = {
|
|
24
30
|
agentAuthStatus: "",
|
|
25
31
|
agentAuthProvider: "",
|
|
@@ -427,9 +433,9 @@ function LocalAgentHostControls({
|
|
|
427
433
|
onSandboxRowPatch
|
|
428
434
|
}) {
|
|
429
435
|
const row = sandboxRow && typeof sandboxRow === "object" ? sandboxRow : {};
|
|
430
|
-
const runLocality = String(row.runLocality || "local").trim().toLowerCase() === "serverless" ? "serverless" : "local";
|
|
431
436
|
const adapter = String(row.adapter || "local-process").trim() || "local-process";
|
|
432
437
|
const agentHost = String(row.agentHost || "").trim();
|
|
438
|
+
const browserOn = ["true", "1", "on", "yes"].includes(String(row.browserAccess || "").trim().toLowerCase());
|
|
433
439
|
const hostOptions = getAgentHostOptions();
|
|
434
440
|
const canPatch = typeof onSandboxRowPatch === "function";
|
|
435
441
|
|
|
@@ -444,36 +450,9 @@ function LocalAgentHostControls({
|
|
|
444
450
|
return (
|
|
445
451
|
<div className="dm-orchestration-config__section dm-workflow-agent-runtime">
|
|
446
452
|
<span>Local agent runtime</span>
|
|
447
|
-
<div className="dm-sandbox-locality-toggle" role="group" aria-label="Run locality">
|
|
448
|
-
{["local", "serverless"].map((mode) => (
|
|
449
|
-
<button
|
|
450
|
-
key={mode}
|
|
451
|
-
type="button"
|
|
452
|
-
className={runLocality === mode ? "is-active" : ""}
|
|
453
|
-
disabled={disabled || !canPatch}
|
|
454
|
-
onClick={() => {
|
|
455
|
-
const fields = { runLocality: mode };
|
|
456
|
-
if (mode === "serverless" && ["local-agent-host", "local-intelligence"].includes(adapter)) {
|
|
457
|
-
fields.adapter = "local-process";
|
|
458
|
-
fields.agentHost = "";
|
|
459
|
-
patchWithClearedAgentAuth(fields);
|
|
460
|
-
return;
|
|
461
|
-
}
|
|
462
|
-
patch(fields);
|
|
463
|
-
}}
|
|
464
|
-
>
|
|
465
|
-
{mode === "local" ? "Local" : "Serverless"}
|
|
466
|
-
</button>
|
|
467
|
-
))}
|
|
468
|
-
</div>
|
|
469
453
|
<p className="dm-orchestration-config__hint">
|
|
470
454
|
Same runtime fields as the Data Model sandbox sidecar. Local agent host uses the Paperclip thin adapter on this machine.
|
|
471
455
|
</p>
|
|
472
|
-
{runLocality === "serverless" && (
|
|
473
|
-
<p className="dm-orchestration-config__hint">
|
|
474
|
-
Serverless delegates execution to the configured scheduler/API Registry row; local CLI auth is not used.
|
|
475
|
-
</p>
|
|
476
|
-
)}
|
|
477
456
|
<label className="dm-orchestration-config__field">
|
|
478
457
|
<span>Execution adapter</span>
|
|
479
458
|
<select
|
|
@@ -482,6 +461,7 @@ function LocalAgentHostControls({
|
|
|
482
461
|
onChange={(event) => {
|
|
483
462
|
const nextAdapter = event.target.value;
|
|
484
463
|
patchWithClearedAgentAuth({
|
|
464
|
+
runLocality: "local",
|
|
485
465
|
adapter: nextAdapter,
|
|
486
466
|
agentHost: nextAdapter === "local-agent-host" ? (agentHost || "claude_local") : ""
|
|
487
467
|
});
|
|
@@ -492,13 +472,17 @@ function LocalAgentHostControls({
|
|
|
492
472
|
))}
|
|
493
473
|
</select>
|
|
494
474
|
</label>
|
|
495
|
-
{
|
|
475
|
+
{adapter === "local-agent-host" && (
|
|
496
476
|
<label className="dm-orchestration-config__field">
|
|
497
477
|
<span>Agent host (Paperclip)</span>
|
|
498
478
|
<select
|
|
499
479
|
value={agentHost}
|
|
500
480
|
disabled={disabled || !canPatch}
|
|
501
|
-
onChange={(event) => patchWithClearedAgentAuth({
|
|
481
|
+
onChange={(event) => patchWithClearedAgentAuth({
|
|
482
|
+
runLocality: "local",
|
|
483
|
+
adapter: "local-agent-host",
|
|
484
|
+
agentHost: event.target.value
|
|
485
|
+
})}
|
|
502
486
|
>
|
|
503
487
|
<option value="">Select host...</option>
|
|
504
488
|
{hostOptions.map((item) => (
|
|
@@ -507,7 +491,7 @@ function LocalAgentHostControls({
|
|
|
507
491
|
</select>
|
|
508
492
|
</label>
|
|
509
493
|
)}
|
|
510
|
-
{
|
|
494
|
+
{adapter === "local-agent-host" && isSandboxLocalAgentHost(row) && (
|
|
511
495
|
<SandboxAgentAuthPanel
|
|
512
496
|
objectId={objectId}
|
|
513
497
|
rowName={rowName}
|
|
@@ -516,10 +500,63 @@ function LocalAgentHostControls({
|
|
|
516
500
|
onPatchDraft={patch}
|
|
517
501
|
/>
|
|
518
502
|
)}
|
|
503
|
+
{adapter === "local-intelligence" && (
|
|
504
|
+
<div className="dm-sandbox-local-intel">
|
|
505
|
+
<label className="dm-orchestration-config__field">
|
|
506
|
+
<span>Concrete model id</span>
|
|
507
|
+
<input
|
|
508
|
+
value={row.localModel || ""}
|
|
509
|
+
disabled={disabled || !canPatch}
|
|
510
|
+
placeholder="gemma3:4b"
|
|
511
|
+
onChange={(event) => patch({ runLocality: "local", localModel: event.target.value })}
|
|
512
|
+
/>
|
|
513
|
+
</label>
|
|
514
|
+
<label className="dm-orchestration-config__field">
|
|
515
|
+
<span>Chat completions URL (optional)</span>
|
|
516
|
+
<input
|
|
517
|
+
value={row.localEndpoint || ""}
|
|
518
|
+
disabled={disabled || !canPatch}
|
|
519
|
+
placeholder="http://127.0.0.1:11434/v1/chat/completions"
|
|
520
|
+
onChange={(event) => patch({ runLocality: "local", localEndpoint: event.target.value })}
|
|
521
|
+
/>
|
|
522
|
+
</label>
|
|
523
|
+
<label className="dm-orchestration-config__field">
|
|
524
|
+
<span>Resolver mode</span>
|
|
525
|
+
<select
|
|
526
|
+
value={String(row.intelligenceAdapterMode || "ollama").trim().toLowerCase()}
|
|
527
|
+
disabled={disabled || !canPatch}
|
|
528
|
+
onChange={(event) => patch({ runLocality: "local", intelligenceAdapterMode: event.target.value })}
|
|
529
|
+
>
|
|
530
|
+
{LOCAL_INTELLIGENCE_MODE_OPTIONS.map((item) => (
|
|
531
|
+
<option key={item.value} value={item.value}>{item.label}</option>
|
|
532
|
+
))}
|
|
533
|
+
</select>
|
|
534
|
+
</label>
|
|
535
|
+
<p className="dm-orchestration-config__hint">
|
|
536
|
+
Uses Instructions + Command as the task payload. With sandbox browser access off, tool intents stay proposals. With browser access on, browser tool intents execute through the local browser bridge before the final JSON response is returned.
|
|
537
|
+
</p>
|
|
538
|
+
{browserOn && (
|
|
539
|
+
<p className="dm-orchestration-config__hint">
|
|
540
|
+
This workflow's AI-agent nodes inherit browser access only when their node-level Network permission is enabled.
|
|
541
|
+
</p>
|
|
542
|
+
)}
|
|
543
|
+
</div>
|
|
544
|
+
)}
|
|
519
545
|
</div>
|
|
520
546
|
);
|
|
521
547
|
}
|
|
522
548
|
|
|
549
|
+
function buildNodeAgentAuthDraft(sandboxRow, config) {
|
|
550
|
+
const agentHost = String(config?.agentHost || sandboxRow?.agentHost || "").trim();
|
|
551
|
+
if (!agentHost) return null;
|
|
552
|
+
return {
|
|
553
|
+
...(sandboxRow || {}),
|
|
554
|
+
runLocality: "local",
|
|
555
|
+
adapter: "local-agent-host",
|
|
556
|
+
agentHost
|
|
557
|
+
};
|
|
558
|
+
}
|
|
559
|
+
|
|
523
560
|
export function OrchestrationNodeConfigPanel({
|
|
524
561
|
node,
|
|
525
562
|
onConfigChange,
|
|
@@ -577,6 +614,28 @@ export function OrchestrationNodeConfigPanel({
|
|
|
577
614
|
|
|
578
615
|
const registryConnected = isApiRegistryTestSuccessful(registryRow);
|
|
579
616
|
const responseMode = config.responseMode || config.mode || "json";
|
|
617
|
+
const nodeAgentAuthDraft = type === "ai-agent" ? buildNodeAgentAuthDraft(sandboxRow, config) : null;
|
|
618
|
+
const canPatchSandboxRow = typeof onSandboxRowPatch === "function";
|
|
619
|
+
const sandboxBrowserOn = ["true", "1", "on", "yes"].includes(String(sandboxRow?.browserAccess || "").trim().toLowerCase());
|
|
620
|
+
const sandboxAdapter = String(sandboxRow?.adapter || "").trim();
|
|
621
|
+
const nodeAdapter = String(config.adapter || "").trim() || sandboxAdapter;
|
|
622
|
+
const nodeUsesLocalIntelligence = nodeAdapter === "local-intelligence";
|
|
623
|
+
|
|
624
|
+
function patchNodeAgentHost(agentHost) {
|
|
625
|
+
const nextHost = String(agentHost || "").trim();
|
|
626
|
+
patchConfig({
|
|
627
|
+
agentHost: nextHost,
|
|
628
|
+
...(nextHost ? { adapter: "local-agent-host" } : {})
|
|
629
|
+
});
|
|
630
|
+
if (nextHost && canPatchSandboxRow) {
|
|
631
|
+
onSandboxRowPatch({
|
|
632
|
+
runLocality: "local",
|
|
633
|
+
adapter: "local-agent-host",
|
|
634
|
+
agentHost: nextHost,
|
|
635
|
+
...EMPTY_AGENT_AUTH_PATCH
|
|
636
|
+
});
|
|
637
|
+
}
|
|
638
|
+
}
|
|
580
639
|
|
|
581
640
|
return (
|
|
582
641
|
<div className="dm-orchestration-config">
|
|
@@ -964,7 +1023,7 @@ export function OrchestrationNodeConfigPanel({
|
|
|
964
1023
|
<select
|
|
965
1024
|
value={config.agentHost || ""}
|
|
966
1025
|
disabled={disabled}
|
|
967
|
-
onChange={(e) =>
|
|
1026
|
+
onChange={(e) => patchNodeAgentHost(e.target.value)}
|
|
968
1027
|
>
|
|
969
1028
|
<option value="">Inherit</option>
|
|
970
1029
|
{Object.entries(HOST_AUTH_CATALOG || {}).map(([slug, host]) => (
|
|
@@ -972,6 +1031,15 @@ export function OrchestrationNodeConfigPanel({
|
|
|
972
1031
|
))}
|
|
973
1032
|
</select>
|
|
974
1033
|
</label>
|
|
1034
|
+
{nodeAgentAuthDraft && isSandboxLocalAgentHost(nodeAgentAuthDraft) && (
|
|
1035
|
+
<SandboxAgentAuthPanel
|
|
1036
|
+
objectId={objectId}
|
|
1037
|
+
rowName={rowName}
|
|
1038
|
+
draft={nodeAgentAuthDraft}
|
|
1039
|
+
disabled={disabled || !canPatchSandboxRow}
|
|
1040
|
+
onPatchDraft={onSandboxRowPatch}
|
|
1041
|
+
/>
|
|
1042
|
+
)}
|
|
975
1043
|
<WorkflowCheckbox
|
|
976
1044
|
checked={config.required !== false}
|
|
977
1045
|
disabled={disabled}
|
|
@@ -982,10 +1050,12 @@ export function OrchestrationNodeConfigPanel({
|
|
|
982
1050
|
<WorkflowCheckbox
|
|
983
1051
|
checked={config.networkAccess === true}
|
|
984
1052
|
disabled={disabled}
|
|
985
|
-
title=
|
|
1053
|
+
title={sandboxBrowserOn && nodeUsesLocalIntelligence
|
|
1054
|
+
? "Network and browser are granted only when this node permission is on and the sandbox row has browser access on."
|
|
1055
|
+
: "Network is granted only when both this and the row's networkAllow are on. The row's browser access inherits through the same gate."}
|
|
986
1056
|
onChange={(checked) => patchConfig({ networkAccess: checked })}
|
|
987
1057
|
>
|
|
988
|
-
Network
|
|
1058
|
+
{sandboxBrowserOn && nodeUsesLocalIntelligence ? "Network + browser" : "Network"}
|
|
989
1059
|
</WorkflowCheckbox>
|
|
990
1060
|
</div>
|
|
991
1061
|
)}
|
|
@@ -1025,8 +1095,15 @@ export function OrchestrationNodeConfigPanel({
|
|
|
1025
1095
|
<WorkflowCheckbox checked={config.canWriteDraft === true} disabled={disabled} onChange={(checked) => patchConfig({ canWriteDraft: checked })}>
|
|
1026
1096
|
Write draft changes only
|
|
1027
1097
|
</WorkflowCheckbox>
|
|
1028
|
-
<WorkflowCheckbox
|
|
1029
|
-
|
|
1098
|
+
<WorkflowCheckbox
|
|
1099
|
+
checked={config.networkAccess === true}
|
|
1100
|
+
disabled={disabled}
|
|
1101
|
+
title={sandboxBrowserOn && nodeUsesLocalIntelligence
|
|
1102
|
+
? "This node gets browser access only when this permission and the sandbox row Browser access toggle are both on."
|
|
1103
|
+
: undefined}
|
|
1104
|
+
onChange={(checked) => patchConfig({ networkAccess: checked })}
|
|
1105
|
+
>
|
|
1106
|
+
{sandboxBrowserOn && nodeUsesLocalIntelligence ? "Allow network + browser access" : "Allow network access"}
|
|
1030
1107
|
</WorkflowCheckbox>
|
|
1031
1108
|
</div>
|
|
1032
1109
|
<KeyValueRows
|
|
@@ -20,9 +20,9 @@
|
|
|
20
20
|
* no second product surface, no terminal emulator — just a uniform
|
|
21
21
|
* readiness bridge.
|
|
22
22
|
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
23
|
+
* The pill represents selected local host readiness. Legacy `reachable`
|
|
24
|
+
* metadata is rendered as Active so Data Model and workflow sidecars stay
|
|
25
|
+
* visually identical after a successful host switch/check path.
|
|
26
26
|
*/
|
|
27
27
|
|
|
28
28
|
import { useCallback, useState } from "react";
|
|
@@ -32,7 +32,7 @@ import { getAgentHostCapabilities } from "@/lib/sandbox-agent-host-catalog";
|
|
|
32
32
|
|
|
33
33
|
const STATUS_LABEL = {
|
|
34
34
|
active: "Active",
|
|
35
|
-
reachable: "
|
|
35
|
+
reachable: "Active",
|
|
36
36
|
stale: "Stale",
|
|
37
37
|
missing: "Missing",
|
|
38
38
|
checking: "Checking",
|
|
@@ -40,10 +40,9 @@ const STATUS_LABEL = {
|
|
|
40
40
|
};
|
|
41
41
|
|
|
42
42
|
function statusKind(status) {
|
|
43
|
-
if (status === "active") return "ok";
|
|
43
|
+
if (status === "active" || status === "reachable") return "ok";
|
|
44
44
|
if (status === "stale") return "warn";
|
|
45
45
|
if (status === "missing") return "bad";
|
|
46
|
-
// "reachable" stays neutral — CLI is installed, but auth is NOT confirmed.
|
|
47
46
|
return "";
|
|
48
47
|
}
|
|
49
48
|
|
|
@@ -62,15 +61,28 @@ export function SandboxAgentAuthPanel({ objectId, rowName, draft, disabled, onPa
|
|
|
62
61
|
const [busy, setBusy] = useState(null); // "status" | "login" | "logout" | null
|
|
63
62
|
const [output, setOutput] = useState(null);
|
|
64
63
|
const [message, setMessage] = useState("");
|
|
64
|
+
const [localAuthState, setLocalAuthState] = useState(null);
|
|
65
65
|
|
|
66
66
|
const capabilities = getAgentHostCapabilities(draft);
|
|
67
67
|
const providerMatchesHost = String(draft?.agentAuthProvider || "").trim() === String(draft?.agentHost || "").trim();
|
|
68
|
+
const localMatchesHost =
|
|
69
|
+
localAuthState?.provider &&
|
|
70
|
+
String(localAuthState.provider || "").trim() === String(capabilities?.slug || "").trim();
|
|
68
71
|
const currentStatus =
|
|
69
|
-
|
|
70
|
-
?
|
|
71
|
-
: "
|
|
72
|
-
|
|
73
|
-
|
|
72
|
+
localMatchesHost && typeof localAuthState?.status === "string" && localAuthState.status.trim()
|
|
73
|
+
? localAuthState.status.trim()
|
|
74
|
+
: providerMatchesHost && typeof draft?.agentAuthStatus === "string" && draft.agentAuthStatus.trim()
|
|
75
|
+
? draft.agentAuthStatus.trim()
|
|
76
|
+
: "unknown";
|
|
77
|
+
const lastChecked = localMatchesHost && localAuthState?.checkedAt
|
|
78
|
+
? localAuthState.checkedAt
|
|
79
|
+
: providerMatchesHost
|
|
80
|
+
? draft?.agentAuthLastChecked || ""
|
|
81
|
+
: "";
|
|
82
|
+
const lastMessage =
|
|
83
|
+
localMatchesHost && typeof localAuthState?.message === "string"
|
|
84
|
+
? localAuthState.message
|
|
85
|
+
: providerMatchesHost ? draft?.agentAuthLastMessage || "" : "";
|
|
74
86
|
const displayMessage = normalizeAuthMessage(message || lastMessage, capabilities?.label)
|
|
75
87
|
|| (currentStatus === "unknown" ? "Run Check or Login to verify this local agent host." : "");
|
|
76
88
|
|
|
@@ -85,15 +97,23 @@ export function SandboxAgentAuthPanel({ objectId, rowName, draft, disabled, onPa
|
|
|
85
97
|
const res = await fetch(endpoint, {
|
|
86
98
|
method: "POST",
|
|
87
99
|
headers: { "content-type": "application/json" },
|
|
88
|
-
body: JSON.stringify({ objectId, name: rowName })
|
|
100
|
+
body: JSON.stringify({ objectId, name: rowName, agentHost: capabilities.slug })
|
|
89
101
|
});
|
|
90
102
|
const payload = await res.json();
|
|
91
103
|
setOutput(payload);
|
|
92
104
|
setMessage(payload.message || (payload.ok ? "Done" : payload.error || "Failed"));
|
|
105
|
+
if (payload.status) {
|
|
106
|
+
setLocalAuthState({
|
|
107
|
+
status: payload.status,
|
|
108
|
+
provider: payload.provider || capabilities.slug,
|
|
109
|
+
checkedAt: payload.checkedAt || new Date().toISOString(),
|
|
110
|
+
message: payload.message || ""
|
|
111
|
+
});
|
|
112
|
+
}
|
|
93
113
|
if (typeof onPatchDraft === "function" && payload.status) {
|
|
94
114
|
onPatchDraft({
|
|
95
115
|
agentAuthStatus: payload.status,
|
|
96
|
-
agentAuthProvider: payload.provider ||
|
|
116
|
+
agentAuthProvider: payload.provider || capabilities.slug,
|
|
97
117
|
agentAuthLastChecked: payload.checkedAt || new Date().toISOString(),
|
|
98
118
|
agentAuthLastExitCode:
|
|
99
119
|
typeof payload.exitCode === "number" ? payload.exitCode : null,
|
|
@@ -107,7 +127,7 @@ export function SandboxAgentAuthPanel({ objectId, rowName, draft, disabled, onPa
|
|
|
107
127
|
setBusy(null);
|
|
108
128
|
}
|
|
109
129
|
},
|
|
110
|
-
[canAct, objectId, rowName, onPatchDraft,
|
|
130
|
+
[canAct, objectId, rowName, onPatchDraft, capabilities?.slug]
|
|
111
131
|
);
|
|
112
132
|
|
|
113
133
|
const onCheckStatus = () =>
|
package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/globals.css
CHANGED
|
@@ -6253,9 +6253,16 @@ body.workspace-rail-collapsed .workspace-builder.dm-workflow-page {
|
|
|
6253
6253
|
.dm-sandbox-config > .dm-cockpit { width: 100%; margin: 12px 0 8px; box-sizing: border-box; }
|
|
6254
6254
|
.dm-radio-row { display: grid; gap: 8px; }
|
|
6255
6255
|
.dm-radio-row label, .dm-check-row { display: grid; grid-template-columns: 16px minmax(0,1fr); align-items: start; column-gap: 8px; color: #1f2937; font-size: 12px; line-height: 1.35; }
|
|
6256
|
+
.dm-check-row-compact { grid-template-columns: 16px max-content 22px; align-items: center; width: fit-content; max-width: 100%; }
|
|
6256
6257
|
.dm-radio-row input[type="radio"], .dm-check-row input[type="checkbox"] { width: 14px; height: 14px; margin: 1px 0 0; padding: 0; box-shadow: none; accent-color: #111827; }
|
|
6257
6258
|
.dm-radio-row span, .dm-check-row span { color: #1f2937; font-size: 12px; font-weight: 500; }
|
|
6258
6259
|
.dm-check-row { cursor: pointer; }
|
|
6260
|
+
.dm-check-row-compact > label { color: #1f2937; font-size: 12px; font-weight: 500; cursor: pointer; }
|
|
6261
|
+
.dm-help-wrap { position: relative; display: inline-flex; align-items: center; min-width: 0; }
|
|
6262
|
+
.dm-icon-help { width: 18px; height: 18px; display: inline-flex; align-items: center; justify-content: center; border: 0; border-radius: 4px; background: transparent; color: #64748b; padding: 0; cursor: help; }
|
|
6263
|
+
.dm-icon-help:hover, .dm-icon-help:focus-visible { background: #f1f5f9; color: #334155; outline: none; }
|
|
6264
|
+
.dm-help-bubble { position: absolute; z-index: 80; left: 22px; top: 50%; transform: translateY(-50%); width: min(280px, calc(100vw - 48px)); display: none; border: 1px solid #cbd5e1; border-radius: 6px; background: #fff; box-shadow: 0 12px 30px rgba(15,23,42,.16); color: #334155; font-size: 11px; font-weight: 500; line-height: 1.4; padding: 8px 9px; }
|
|
6265
|
+
.dm-help-wrap:hover .dm-help-bubble, .dm-help-bubble.is-open { display: block; }
|
|
6259
6266
|
.dm-select { position: relative; width: 100%; min-width: 0; font-size: 11px; }
|
|
6260
6267
|
.dm-select-trigger { width: 100%; min-height: 32px; display: flex; align-items: center; justify-content: space-between; gap: 8px; border: 1px solid #cbd5e1; border-radius: 7px; background: #fff; color: #111827; box-shadow: 0 1px 2px rgba(15,23,42,.05); font: inherit; font-size: 11px; padding: 6px 10px; text-align: left; cursor: pointer; transition: border-color .12s, box-shadow .12s, background .12s; }
|
|
6261
6268
|
.dm-select-trigger:hover:not(:disabled) { border-color: #94a3b8; box-shadow: 0 2px 8px rgba(15,23,42,.08); }
|