@growthub/cli 0.14.0 → 0.14.2

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.
Files changed (40) hide show
  1. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/helper/apply/route.js +99 -2
  2. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/helper/query/route.js +1 -0
  3. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/sandbox-agent-auth/login/route.js +3 -2
  4. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/sandbox-agent-auth/logout/route.js +3 -2
  5. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/sandbox-agent-auth/status/route.js +3 -2
  6. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/sandbox-run/route.js +84 -10
  7. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/components/WorkspaceHelperSetupModal.jsx +2 -2
  8. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/AgentSwarmPanel.jsx +107 -34
  9. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/DataModelShell.jsx +72 -15
  10. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/HelperSidecar.jsx +264 -22
  11. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/OrchestrationGraphCanvas.jsx +81 -10
  12. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/OrchestrationNodeConfigPanel.jsx +179 -117
  13. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/SandboxAgentAuthPanel.jsx +34 -14
  14. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/SidecarExpandView.jsx +37 -0
  15. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/SwarmRunCockpit.jsx +625 -0
  16. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/helper-commands.js +150 -0
  17. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/globals.css +136 -3
  18. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/workflows/WorkflowSurface.jsx +61 -13
  19. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/docs/sandbox-environment-primitive.md +26 -0
  20. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/sandboxes/adapters/local-intelligence-browser-access.js +516 -0
  21. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/sandboxes/default-local-agent-host.js +224 -11
  22. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/sandboxes/default-local-intelligence.js +4 -0
  23. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/sandboxes/default-local-process.js +3 -1
  24. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/sandboxes/index.js +1 -0
  25. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/sandboxes/sandbox-adapter-registry.js +5 -1
  26. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/data-model/field-contracts.js +1 -0
  27. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/orchestration-agent-swarm.js +254 -4
  28. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/orchestration-graph-runner.js +3 -0
  29. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/orchestration-graph.js +10 -2
  30. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/orchestration-run-console.js +412 -1
  31. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/sandbox-agent-auth.js +82 -27
  32. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/sandbox-serverless-flow.js +4 -2
  33. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-data-model.js +1 -0
  34. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-helper.js +23 -0
  35. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-metadata-store.js +8 -6
  36. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-schema.js +6 -0
  37. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-swarm-proposal.js +554 -0
  38. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/package-lock.json +364 -0
  39. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/package.json +1 -0
  40. package/package.json +1 -1
@@ -1,6 +1,7 @@
1
1
  "use client";
2
2
 
3
3
  import { useMemo, useState } from "react";
4
+ import { Check } from "lucide-react";
4
5
  import {
5
6
  detectFieldIdsFromLastResponse,
6
7
  FILTER_CONJUNCTIONS,
@@ -19,6 +20,12 @@ const LOCAL_AGENT_ADAPTERS = [
19
20
  { value: "local-agent-host", label: "Local agent host" },
20
21
  { value: "local-intelligence", label: "Local intelligence" }
21
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
+ ];
22
29
  const EMPTY_AGENT_AUTH_PATCH = {
23
30
  agentAuthStatus: "",
24
31
  agentAuthProvider: "",
@@ -28,6 +35,23 @@ const EMPTY_AGENT_AUTH_PATCH = {
28
35
  agentAuthLastLoginUrl: ""
29
36
  };
30
37
 
38
+ function WorkflowCheckbox({ checked, disabled, onChange, children, title }) {
39
+ return (
40
+ <label className="dm-orchestration-config__field dm-orchestration-config__field-inline dm-workflow-check" title={title}>
41
+ <input
42
+ type="checkbox"
43
+ checked={checked}
44
+ disabled={disabled}
45
+ onChange={(event) => onChange?.(event.target.checked)}
46
+ />
47
+ <span className="dm-workflow-check__box" aria-hidden="true">
48
+ {checked ? <Check size={13} strokeWidth={2.4} /> : null}
49
+ </span>
50
+ <span>{children}</span>
51
+ </label>
52
+ );
53
+ }
54
+
31
55
  function getAgentHostOptions() {
32
56
  return Object.entries(HOST_AUTH_CATALOG || {}).map(([slug, host]) => ({
33
57
  value: slug,
@@ -409,9 +433,9 @@ function LocalAgentHostControls({
409
433
  onSandboxRowPatch
410
434
  }) {
411
435
  const row = sandboxRow && typeof sandboxRow === "object" ? sandboxRow : {};
412
- const runLocality = String(row.runLocality || "local").trim().toLowerCase() === "serverless" ? "serverless" : "local";
413
436
  const adapter = String(row.adapter || "local-process").trim() || "local-process";
414
437
  const agentHost = String(row.agentHost || "").trim();
438
+ const browserOn = ["true", "1", "on", "yes"].includes(String(row.browserAccess || "").trim().toLowerCase());
415
439
  const hostOptions = getAgentHostOptions();
416
440
  const canPatch = typeof onSandboxRowPatch === "function";
417
441
 
@@ -426,36 +450,9 @@ function LocalAgentHostControls({
426
450
  return (
427
451
  <div className="dm-orchestration-config__section dm-workflow-agent-runtime">
428
452
  <span>Local agent runtime</span>
429
- <div className="dm-sandbox-locality-toggle" role="group" aria-label="Run locality">
430
- {["local", "serverless"].map((mode) => (
431
- <button
432
- key={mode}
433
- type="button"
434
- className={runLocality === mode ? "is-active" : ""}
435
- disabled={disabled || !canPatch}
436
- onClick={() => {
437
- const fields = { runLocality: mode };
438
- if (mode === "serverless" && ["local-agent-host", "local-intelligence"].includes(adapter)) {
439
- fields.adapter = "local-process";
440
- fields.agentHost = "";
441
- patchWithClearedAgentAuth(fields);
442
- return;
443
- }
444
- patch(fields);
445
- }}
446
- >
447
- {mode === "local" ? "Local" : "Serverless"}
448
- </button>
449
- ))}
450
- </div>
451
453
  <p className="dm-orchestration-config__hint">
452
454
  Same runtime fields as the Data Model sandbox sidecar. Local agent host uses the Paperclip thin adapter on this machine.
453
455
  </p>
454
- {runLocality === "serverless" && (
455
- <p className="dm-orchestration-config__hint">
456
- Serverless delegates execution to the configured scheduler/API Registry row; local CLI auth is not used.
457
- </p>
458
- )}
459
456
  <label className="dm-orchestration-config__field">
460
457
  <span>Execution adapter</span>
461
458
  <select
@@ -464,6 +461,7 @@ function LocalAgentHostControls({
464
461
  onChange={(event) => {
465
462
  const nextAdapter = event.target.value;
466
463
  patchWithClearedAgentAuth({
464
+ runLocality: "local",
467
465
  adapter: nextAdapter,
468
466
  agentHost: nextAdapter === "local-agent-host" ? (agentHost || "claude_local") : ""
469
467
  });
@@ -474,13 +472,17 @@ function LocalAgentHostControls({
474
472
  ))}
475
473
  </select>
476
474
  </label>
477
- {runLocality === "local" && adapter === "local-agent-host" && (
475
+ {adapter === "local-agent-host" && (
478
476
  <label className="dm-orchestration-config__field">
479
477
  <span>Agent host (Paperclip)</span>
480
478
  <select
481
479
  value={agentHost}
482
480
  disabled={disabled || !canPatch}
483
- onChange={(event) => patchWithClearedAgentAuth({ agentHost: event.target.value })}
481
+ onChange={(event) => patchWithClearedAgentAuth({
482
+ runLocality: "local",
483
+ adapter: "local-agent-host",
484
+ agentHost: event.target.value
485
+ })}
484
486
  >
485
487
  <option value="">Select host...</option>
486
488
  {hostOptions.map((item) => (
@@ -489,7 +491,7 @@ function LocalAgentHostControls({
489
491
  </select>
490
492
  </label>
491
493
  )}
492
- {runLocality === "local" && adapter === "local-agent-host" && isSandboxLocalAgentHost(row) && (
494
+ {adapter === "local-agent-host" && isSandboxLocalAgentHost(row) && (
493
495
  <SandboxAgentAuthPanel
494
496
  objectId={objectId}
495
497
  rowName={rowName}
@@ -498,10 +500,63 @@ function LocalAgentHostControls({
498
500
  onPatchDraft={patch}
499
501
  />
500
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
+ )}
501
545
  </div>
502
546
  );
503
547
  }
504
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
+
505
560
  export function OrchestrationNodeConfigPanel({
506
561
  node,
507
562
  onConfigChange,
@@ -559,6 +614,28 @@ export function OrchestrationNodeConfigPanel({
559
614
 
560
615
  const registryConnected = isApiRegistryTestSuccessful(registryRow);
561
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
+ }
562
639
 
563
640
  return (
564
641
  <div className="dm-orchestration-config">
@@ -696,24 +773,20 @@ export function OrchestrationNodeConfigPanel({
696
773
  Latest registry test: {registryRow.status}
697
774
  </span>
698
775
  )}
699
- <label className="dm-orchestration-config__field dm-orchestration-config__field-inline">
700
- <input
701
- type="checkbox"
702
- checked={config.writeLastResponse !== false}
703
- disabled={disabled}
704
- onChange={(e) => patchConfig({ writeLastResponse: e.target.checked })}
705
- />
706
- <span>Write lastResponse on success</span>
707
- </label>
708
- <label className="dm-orchestration-config__field dm-orchestration-config__field-inline">
709
- <input
710
- type="checkbox"
711
- checked={config.writeSourceRecord !== false}
712
- disabled={disabled}
713
- onChange={(e) => patchConfig({ writeSourceRecord: e.target.checked })}
714
- />
715
- <span>Write source record history</span>
716
- </label>
776
+ <WorkflowCheckbox
777
+ checked={config.writeLastResponse !== false}
778
+ disabled={disabled}
779
+ onChange={(checked) => patchConfig({ writeLastResponse: checked })}
780
+ >
781
+ Write lastResponse on success
782
+ </WorkflowCheckbox>
783
+ <WorkflowCheckbox
784
+ checked={config.writeSourceRecord !== false}
785
+ disabled={disabled}
786
+ onChange={(checked) => patchConfig({ writeSourceRecord: checked })}
787
+ >
788
+ Write source record history
789
+ </WorkflowCheckbox>
717
790
  <label className="dm-orchestration-config__field">
718
791
  <span>Success HTTP codes</span>
719
792
  <input
@@ -882,15 +955,13 @@ export function OrchestrationNodeConfigPanel({
882
955
  )}
883
956
  </div>
884
957
  )}
885
- <label className="dm-orchestration-config__field dm-orchestration-config__field-inline">
886
- <input
887
- type="checkbox"
888
- checked={config.confirmationRequired === true}
889
- disabled={disabled || config.destructive === true}
890
- onChange={(e) => patchConfig({ confirmationRequired: e.target.checked })}
891
- />
892
- <span>Require confirmation before destructive or version-changing execution</span>
893
- </label>
958
+ <WorkflowCheckbox
959
+ checked={config.confirmationRequired === true}
960
+ disabled={disabled || config.destructive === true}
961
+ onChange={(checked) => patchConfig({ confirmationRequired: checked })}
962
+ >
963
+ Require confirmation before destructive or version-changing execution
964
+ </WorkflowCheckbox>
894
965
  <p className="dm-orchestration-config__hint">
895
966
  Data actions bind only to this workspace data model. Execution resolves the latest object schema at run time.
896
967
  </p>
@@ -952,7 +1023,7 @@ export function OrchestrationNodeConfigPanel({
952
1023
  <select
953
1024
  value={config.agentHost || ""}
954
1025
  disabled={disabled}
955
- onChange={(e) => patchConfig({ agentHost: e.target.value })}
1026
+ onChange={(e) => patchNodeAgentHost(e.target.value)}
956
1027
  >
957
1028
  <option value="">Inherit</option>
958
1029
  {Object.entries(HOST_AUTH_CATALOG || {}).map(([slug, host]) => (
@@ -960,27 +1031,32 @@ export function OrchestrationNodeConfigPanel({
960
1031
  ))}
961
1032
  </select>
962
1033
  </label>
963
- <label className="dm-orchestration-config__field dm-orchestration-config__field-inline">
964
- <input
965
- type="checkbox"
966
- checked={config.required !== false}
967
- disabled={disabled}
968
- onChange={(e) => patchConfig({ required: e.target.checked })}
1034
+ {nodeAgentAuthDraft && isSandboxLocalAgentHost(nodeAgentAuthDraft) && (
1035
+ <SandboxAgentAuthPanel
1036
+ objectId={objectId}
1037
+ rowName={rowName}
1038
+ draft={nodeAgentAuthDraft}
1039
+ disabled={disabled || !canPatchSandboxRow}
1040
+ onPatchDraft={onSandboxRowPatch}
969
1041
  />
970
- <span>Required</span>
971
- </label>
972
- <label
973
- className="dm-orchestration-config__field dm-orchestration-config__field-inline"
974
- title="Network is granted only when both this and the row's networkAllow are on."
1042
+ )}
1043
+ <WorkflowCheckbox
1044
+ checked={config.required !== false}
1045
+ disabled={disabled}
1046
+ onChange={(checked) => patchConfig({ required: checked })}
975
1047
  >
976
- <input
977
- type="checkbox"
978
- checked={config.networkAccess === true}
979
- disabled={disabled}
980
- onChange={(e) => patchConfig({ networkAccess: e.target.checked })}
981
- />
982
- <span>Network</span>
983
- </label>
1048
+ Required
1049
+ </WorkflowCheckbox>
1050
+ <WorkflowCheckbox
1051
+ checked={config.networkAccess === true}
1052
+ disabled={disabled}
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."}
1056
+ onChange={(checked) => patchConfig({ networkAccess: checked })}
1057
+ >
1058
+ {sandboxBrowserOn && nodeUsesLocalIntelligence ? "Network + browser" : "Network"}
1059
+ </WorkflowCheckbox>
984
1060
  </div>
985
1061
  )}
986
1062
 
@@ -1013,33 +1089,22 @@ export function OrchestrationNodeConfigPanel({
1013
1089
  </label>
1014
1090
  <div className="dm-orchestration-config__section">
1015
1091
  <span>Permissions</span>
1016
- <label className="dm-orchestration-config__field dm-orchestration-config__field-inline">
1017
- <input
1018
- type="checkbox"
1019
- checked={config.canReadWorkspace !== false}
1020
- disabled={disabled}
1021
- onChange={(e) => patchConfig({ canReadWorkspace: e.target.checked })}
1022
- />
1023
- <span>Read workspace data</span>
1024
- </label>
1025
- <label className="dm-orchestration-config__field dm-orchestration-config__field-inline">
1026
- <input
1027
- type="checkbox"
1028
- checked={config.canWriteDraft === true}
1029
- disabled={disabled}
1030
- onChange={(e) => patchConfig({ canWriteDraft: e.target.checked })}
1031
- />
1032
- <span>Write draft changes only</span>
1033
- </label>
1034
- <label className="dm-orchestration-config__field dm-orchestration-config__field-inline">
1035
- <input
1036
- type="checkbox"
1037
- checked={config.networkAccess === true}
1038
- disabled={disabled}
1039
- onChange={(e) => patchConfig({ networkAccess: e.target.checked })}
1040
- />
1041
- <span>Allow network access</span>
1042
- </label>
1092
+ <WorkflowCheckbox checked={config.canReadWorkspace !== false} disabled={disabled} onChange={(checked) => patchConfig({ canReadWorkspace: checked })}>
1093
+ Read workspace data
1094
+ </WorkflowCheckbox>
1095
+ <WorkflowCheckbox checked={config.canWriteDraft === true} disabled={disabled} onChange={(checked) => patchConfig({ canWriteDraft: checked })}>
1096
+ Write draft changes only
1097
+ </WorkflowCheckbox>
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"}
1107
+ </WorkflowCheckbox>
1043
1108
  </div>
1044
1109
  <KeyValueRows
1045
1110
  label="Output fields"
@@ -1176,10 +1241,9 @@ export function OrchestrationNodeConfigPanel({
1176
1241
  <span>Message</span>
1177
1242
  <textarea rows={6} value={config.message || ""} disabled={disabled} onChange={(e) => patchConfig({ message: e.target.value })} />
1178
1243
  </label>
1179
- <label className="dm-orchestration-config__field dm-orchestration-config__field-inline">
1180
- <input type="checkbox" checked={config.requireApproval !== false} disabled={disabled} onChange={(e) => patchConfig({ requireApproval: e.target.checked })} />
1181
- <span>Require approval before sending</span>
1182
- </label>
1244
+ <WorkflowCheckbox checked={config.requireApproval !== false} disabled={disabled} onChange={(checked) => patchConfig({ requireApproval: checked })}>
1245
+ Require approval before sending
1246
+ </WorkflowCheckbox>
1183
1247
  </>
1184
1248
  )}
1185
1249
  {config.action === "code-function" && (
@@ -1216,10 +1280,9 @@ export function OrchestrationNodeConfigPanel({
1216
1280
  valuePlaceholder="Field type or help text"
1217
1281
  onChange={(fields) => patchConfig({ fields })}
1218
1282
  />
1219
- <label className="dm-orchestration-config__field dm-orchestration-config__field-inline">
1220
- <input type="checkbox" checked={config.required !== false} disabled={disabled} onChange={(e) => patchConfig({ required: e.target.checked })} />
1221
- <span>Require response before continuing</span>
1222
- </label>
1283
+ <WorkflowCheckbox checked={config.required !== false} disabled={disabled} onChange={(checked) => patchConfig({ required: checked })}>
1284
+ Require response before continuing
1285
+ </WorkflowCheckbox>
1223
1286
  </div>
1224
1287
  )}
1225
1288
 
@@ -1233,10 +1296,9 @@ export function OrchestrationNodeConfigPanel({
1233
1296
  <span>Sample response</span>
1234
1297
  <textarea rows={5} value={config.sampleResponse || ""} disabled={disabled} onChange={(e) => patchConfig({ sampleResponse: e.target.value })} />
1235
1298
  </label>
1236
- <label className="dm-orchestration-config__field dm-orchestration-config__field-inline">
1237
- <input type="checkbox" checked={config.blockPublishOnFailure !== false} disabled={disabled} onChange={(e) => patchConfig({ blockPublishOnFailure: e.target.checked })} />
1238
- <span>Block Publish unless this step passes</span>
1239
- </label>
1299
+ <WorkflowCheckbox checked={config.blockPublishOnFailure !== false} disabled={disabled} onChange={(checked) => patchConfig({ blockPublishOnFailure: checked })}>
1300
+ Block Publish unless this step passes
1301
+ </WorkflowCheckbox>
1240
1302
  </div>
1241
1303
  )}
1242
1304
 
@@ -20,9 +20,9 @@
20
20
  * no second product surface, no terminal emulator — just a uniform
21
21
  * readiness bridge.
22
22
  *
23
- * Status values stamped on the row are intentionally distinct between
24
- * confirmed-authenticated ("active") and merely-installed ("reachable")
25
- * so the pill cannot overclaim auth from a `--version` probe.
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: "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
- providerMatchesHost && typeof draft?.agentAuthStatus === "string" && draft.agentAuthStatus.trim()
70
- ? draft.agentAuthStatus.trim()
71
- : "unknown";
72
- const lastChecked = providerMatchesHost ? draft?.agentAuthLastChecked || "" : "";
73
- const lastMessage = providerMatchesHost ? draft?.agentAuthLastMessage || "" : "";
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 || draft?.agentHost || "unknown",
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, draft?.agentHost]
130
+ [canAct, objectId, rowName, onPatchDraft, capabilities?.slug]
111
131
  );
112
132
 
113
133
  const onCheckStatus = () =>
@@ -0,0 +1,37 @@
1
+ "use client";
2
+
3
+ /**
4
+ * SidecarExpandView — full-width takeover WITHIN the existing helper sidecar
5
+ * system (SWARM_RUN_CONTRACT_V1, Phase 5).
6
+ *
7
+ * Not a modal and not a route: the parent HelperSidecar widens its own aside
8
+ * while this view is active and renders this wrapper inside the same body.
9
+ * Back returns to the prior sidecar view; Esc collapses (handled by the
10
+ * parent so it composes with the sidecar's existing Esc-to-close); the
11
+ * sidecar close button keeps closing the whole sidecar.
12
+ *
13
+ * Reuses the existing sidecar header/body grammar — no new modal stack, no
14
+ * new visual language.
15
+ */
16
+
17
+ import { ArrowLeft } from "lucide-react";
18
+
19
+ export function SidecarExpandView({ title, onBack, children }) {
20
+ return (
21
+ <div className="dm-swarm-expand" data-sidecar-expand="">
22
+ <div className="dm-sidecar-header dm-swarm-expand-head">
23
+ <button
24
+ type="button"
25
+ className="dm-sidecar-icon-btn"
26
+ onClick={onBack}
27
+ aria-label="Back"
28
+ title="Back (Esc)"
29
+ >
30
+ <ArrowLeft size={14} />
31
+ </button>
32
+ <span className="dm-sidecar-title">{title}</span>
33
+ </div>
34
+ <div className="dm-swarm-expand-body">{children}</div>
35
+ </div>
36
+ );
37
+ }