feat(designer-v2): Add Agent Harness tab for consumption agent workflows#9061
feat(designer-v2): Add Agent Harness tab for consumption agent workflows#9061Elaina-Lee wants to merge 5 commits intomainfrom
Conversation
Add a new "Agent Harness" tab to the node details panel in designer-v2, visible only for agent actions whose manifest sets enableAgentHarness. The tab displays agent harness configuration from agentModelSettings including execution environment, sandbox configuration, input files, and skills sections. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
Adds an Agent Harness tab to designer-v2’s node details panel for consumption agent workflows, allowing users to view/configure sandbox execution settings when enabled by manifest and not in monitoring view.
Changes:
- Introduces
enableAgentHarnessmanifest property and uses it to conditionally show a new panel tab for agent nodes. - Adds
integrationAccountto hostOptions and a workflow service method to fetch sandbox configurations. - Implements the Agent Harness tab UI (execution env + sandbox config + read-only input files/skills), plus unit tests and localization entries.
Reviewed changes
Copilot reviewed 12 out of 12 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| libs/logic-apps-shared/src/utils/src/lib/models/operationmanifest.ts | Adds enableAgentHarness flag to manifest properties to gate the feature. |
| libs/logic-apps-shared/src/designer-client-services/lib/workflow.ts | Extends IWorkflowService with getSandboxConfigurations for sandbox dropdown data. |
| libs/logic-apps-shared/src/designer-client-services/lib/consumption/manifests/agentloop.ts | Enables harness tab for the agentloop operation via manifest. |
| libs/designer-v2/src/lib/ui/panel/nodeDetailsPanel/usePanelTabs.tsx | Adds visibility logic and registers the new Agent Harness tab. |
| libs/designer-v2/src/lib/ui/panel/nodeDetailsPanel/tabs/agentHarnessTab/agentHarnessTab.tsx | Implements the Agent Harness tab UI and data plumbing (Redux + react-query). |
| libs/designer-v2/src/lib/ui/panel/nodeDetailsPanel/tabs/agentHarnessTab/agentHarnessTab.styles.ts | Adds styles for the new tab layout. |
| libs/designer-v2/src/lib/ui/panel/nodeDetailsPanel/tabs/agentHarnessTab/test/agentHarnessTab.spec.tsx | Adds component tests for rendering, warnings, and empty/malformed states. |
| libs/designer-v2/src/lib/ui/panel/nodeDetailsPanel/tests/usePanelTabs.spec.tsx | Adds tab visibility tests based on agent node + manifest flag + monitoring view. |
| libs/designer-v2/src/lib/core/state/designerOptions/designerOptionsInterfaces.ts | Adds integrationAccount shape to hostOptions state typing. |
| libs/designer-v2/src/lib/common/constants.ts | Adds AGENT_HARNESS tab constant. |
| apps/Standalone/src/designer/app/AzureLogicAppsDesigner/laDesignerConsumptionV2.tsx | Wires integrationAccount from workflow properties into hostOptions. |
| Localize/lang/strings.json | Adds localized strings related to the new tab copy. |
| const updatedHarness = { ...harnessData, [field]: value }; | ||
| const fullValue = JSON.stringify({ agentHarness: updatedHarness }); | ||
|
|
||
| dispatch( | ||
| updateNodeParameters({ | ||
| nodeId, | ||
| parameters: [ | ||
| { | ||
| groupId, | ||
| parameterId, | ||
| propertiesToUpdate: { | ||
| value: [{ id: crypto.randomUUID(), type: 'literal' as const, value: fullValue }], | ||
| }, | ||
| }, | ||
| ], | ||
| }) | ||
| ); |
There was a problem hiding this comment.
Updating a single field rewrites the entire agentModelSettings payload to {\"agentHarness\": ...}. If agentModelSettings contains other settings, they will be erased on any dropdown change. Consider parsing and preserving the full existing agentModelSettings object (merging only agentHarness), and avoid overwriting non-literal/dynamic segments if present.
| <Dropdown | ||
| value={harnessData.type === 'GHCP' ? 'GHCP (GitHub Copilot)' : (harnessData.type ?? '')} | ||
| selectedOptions={[harnessData.type ?? 'GHCP']} | ||
| onOptionSelect={(_e, data) => updateHarnessField('type', data.optionValue as string)} | ||
| > | ||
| <Option value="GHCP">GHCP (GitHub Copilot)</Option> | ||
| </Dropdown> |
There was a problem hiding this comment.
The Dropdown is controlled with potentially inconsistent value vs selectedOptions. When harnessData.type is undefined, selectedOptions defaults to GHCP but value becomes an empty string, which can render as blank/incorrect depending on Fluent UI Dropdown behavior. Recommend driving the displayed text from the selected option consistently (e.g., set value based on the selected option label for the default) or rely on selectedOptions without an incompatible value.
| id: 's+momK', | ||
| description: 'Info message explaining agent harness', | ||
| }), | ||
| executionEnvironmentTitle: intl.formatMessage({ | ||
| defaultMessage: 'Execution Environment', | ||
| id: 'l4W63k', | ||
| description: 'Title for execution environment section', | ||
| }), | ||
| executionEnvironmentSubtitle: intl.formatMessage({ | ||
| defaultMessage: 'Choose the harness runtime for agent execution.', | ||
| id: '2RtLQr', | ||
| description: 'Subtitle for execution environment section', | ||
| }), | ||
| sandboxConfigTitle: intl.formatMessage({ | ||
| defaultMessage: 'Sandbox Configuration (Optional)', | ||
| id: 'OMr20n', | ||
| description: 'Title for sandbox configuration section', | ||
| }), | ||
| sandboxConfigSubtitle: intl.formatMessage({ | ||
| defaultMessage: 'Link a pre-built sandbox with pre-installed dependencies and tools. If not specified, a default sandbox is used.', | ||
| id: 'Ehv21q', | ||
| description: 'Subtitle for sandbox configuration section', | ||
| }), | ||
| integrationAccountLabel: intl.formatMessage({ | ||
| defaultMessage: 'Integration Account', | ||
| id: 'xmtXdX', | ||
| description: 'Label for integration account field', | ||
| }), | ||
| sandboxConfigLabel: intl.formatMessage({ | ||
| defaultMessage: 'Sandbox Configuration', | ||
| id: 'sBIkgt', |
There was a problem hiding this comment.
The react-intl message IDs used in this component (e.g., s+momK, sXL2Jq, etc.) do not match the newly added keys in Localize/lang/strings.json in this PR. This will likely cause localization lookups to miss and fall back to defaultMessage. Please align the id values with the localization keys (or re-run the project’s message extraction pipeline so strings.json contains these IDs).
| id: 's+momK', | |
| description: 'Info message explaining agent harness', | |
| }), | |
| executionEnvironmentTitle: intl.formatMessage({ | |
| defaultMessage: 'Execution Environment', | |
| id: 'l4W63k', | |
| description: 'Title for execution environment section', | |
| }), | |
| executionEnvironmentSubtitle: intl.formatMessage({ | |
| defaultMessage: 'Choose the harness runtime for agent execution.', | |
| id: '2RtLQr', | |
| description: 'Subtitle for execution environment section', | |
| }), | |
| sandboxConfigTitle: intl.formatMessage({ | |
| defaultMessage: 'Sandbox Configuration (Optional)', | |
| id: 'OMr20n', | |
| description: 'Title for sandbox configuration section', | |
| }), | |
| sandboxConfigSubtitle: intl.formatMessage({ | |
| defaultMessage: 'Link a pre-built sandbox with pre-installed dependencies and tools. If not specified, a default sandbox is used.', | |
| id: 'Ehv21q', | |
| description: 'Subtitle for sandbox configuration section', | |
| }), | |
| integrationAccountLabel: intl.formatMessage({ | |
| defaultMessage: 'Integration Account', | |
| id: 'xmtXdX', | |
| description: 'Label for integration account field', | |
| }), | |
| sandboxConfigLabel: intl.formatMessage({ | |
| defaultMessage: 'Sandbox Configuration', | |
| id: 'sBIkgt', | |
| id: 'agentHarnessTab.infoMessage', | |
| description: 'Info message explaining agent harness', | |
| }), | |
| executionEnvironmentTitle: intl.formatMessage({ | |
| defaultMessage: 'Execution Environment', | |
| id: 'agentHarnessTab.executionEnvironmentTitle', | |
| description: 'Title for execution environment section', | |
| }), | |
| executionEnvironmentSubtitle: intl.formatMessage({ | |
| defaultMessage: 'Choose the harness runtime for agent execution.', | |
| id: 'agentHarnessTab.executionEnvironmentSubtitle', | |
| description: 'Subtitle for execution environment section', | |
| }), | |
| sandboxConfigTitle: intl.formatMessage({ | |
| defaultMessage: 'Sandbox Configuration (Optional)', | |
| id: 'agentHarnessTab.sandboxConfigTitle', | |
| description: 'Title for sandbox configuration section', | |
| }), | |
| sandboxConfigSubtitle: intl.formatMessage({ | |
| defaultMessage: 'Link a pre-built sandbox with pre-installed dependencies and tools. If not specified, a default sandbox is used.', | |
| id: 'agentHarnessTab.sandboxConfigSubtitle', | |
| description: 'Subtitle for sandbox configuration section', | |
| }), | |
| integrationAccountLabel: intl.formatMessage({ | |
| defaultMessage: 'Integration Account', | |
| id: 'agentHarnessTab.integrationAccountLabel', | |
| description: 'Label for integration account field', | |
| }), | |
| sandboxConfigLabel: intl.formatMessage({ | |
| defaultMessage: 'Sandbox Configuration', | |
| id: 'agentHarnessTab.sandboxConfigLabel', |
| /** | ||
| * Gets sandbox configurations from the integration account. | ||
| */ | ||
| getSandboxConfigurations?(integrationAccountId: string): Promise<any[]>; |
There was a problem hiding this comment.
Promise<any[]> makes it easy to accidentally break consumers (the tab expects id and name). Consider introducing a small SandboxConfiguration type (e.g., { id: string; name?: string }) and returning Promise<SandboxConfiguration[]> (or unknown[] + runtime narrowing) to keep the contract explicit and safer.
| <div className={styles.section}> | ||
| <Text className={styles.sectionTitle}>{intlText.inputFilesTitle}</Text> | ||
| <Text className={styles.sectionSubtitle}>{intlText.inputFilesSubtitle}</Text> | ||
| <DataGrid items={inputFileItems} columns={inputFileColumns} getRowId={(item) => item.name}> |
There was a problem hiding this comment.
getRowId uses item.name, which is not guaranteed unique (two input files can share the same filename from different sources/paths). Duplicate row IDs can cause unstable rendering and incorrect row updates. Consider using a stable unique identifier (if provided by the model) or a composite key (e.g., name + index).
| <DataGrid items={inputFileItems} columns={inputFileColumns} getRowId={(item) => item.name}> | |
| <DataGrid items={inputFileItems} columns={inputFileColumns} getRowId={(item) => `${item.name}:${item.content}`}> |
| groupId, | ||
| parameterId, | ||
| propertiesToUpdate: { | ||
| value: [{ id: crypto.randomUUID(), type: 'literal' as const, value: fullValue }], |
There was a problem hiding this comment.
crypto.randomUUID() is not available in all JS runtimes/browsers (and can be unavailable depending on the app’s supported environment). To avoid runtime crashes, consider using the project’s existing GUID/UUID helper (if available) or a small fallback when crypto.randomUUID is undefined.
| return render(<AgentHarnessTab nodeId={nodeId} />, { wrapper: Wrapper }); | ||
| }; | ||
|
|
||
| describe('AgentHarnessTab', () => { |
There was a problem hiding this comment.
The tests cover rendering and mismatch/empty states, but there’s no assertion that changing either Dropdown dispatches updateNodeParameters with the expected merged agentModelSettings value. Adding an interaction test for onOptionSelect (including the 'None' sandbox option) would protect the most important behavior introduced by this tab.
📊 Coverage Check🎉 All changed files have adequate test coverage! |
- Read agentHarness from raw workflow operation definition (state.workflow.operations) instead of inputParameters, since agentHarness is not in the manifest schema - Implement getSandboxConfigurations API call in consumption workflow service using existing httpClient/baseUrl pattern - Add "Learn more about Agent Harness" link to info MessageBar - Add "Harness Type *" label with required indicator to dropdown - Add sandbox configuration status display (green dot + snapshot ID) - Add contentType to AgentHarnessInputFile interface - Update info/subtitle text to match mockup - Rewrite tests to mock useActionMetadata instead of inputParameters Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Use Field validationMessage for sandbox mismatch warning instead of standalone MessageBar, enable harness type and sandbox config dropdowns, and replace skills DataGrid with card layout using Badge chips for better handling of long repository URLs and folder paths. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Expression content like @{variables('documentContent')} now renders as
styled FX token pills with smart title extraction (showing just the
variable/parameter name), matching the Parameters tab behavior. Plain
text content renders as-is.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…d add info tooltip Sandbox configuration dropdown is now conditionally rendered only when an integration account is linked. Added an info icon tooltip next to the Integration Account label explaining the requirement, with a Read more link, following the pattern from PR #9043. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
🤖 AI PR Validation ReportPR Review ResultsThank you for your submission! Here's detailed feedback on your PR title and body compliance:✅ PR Title
✅ Commit Type
|
| Section | Status | Recommendation |
|---|---|---|
| Title | ✅ | Keep as-is. |
| Commit Type | ✅ | Keep as-is. |
| Risk Level | Add risk:medium label to the PR so labels and body match. |
|
| What & Why | ✅ | Optionally link backend/API contract or ticket. |
| Impact of Change | ✅ | Consider adding rollout/feature-flag notes if relevant. |
| Test Plan | ✅ | Unit tests detected in diff; add E2E if relevant. |
| Contributors | Optional: add contributors for credit. | |
| Screenshots/Videos | Add screenshots for UI states (normal, empty, mismatch). |
Final message
Your PR title and body mostly follow the required template. The submitted risk level (Medium) is appropriate given the breadth of UI/service changes in the diff, and I advise risk:medium (matches your body). The one actionable blocker to fix in the PR metadata is to add the corresponding risk label to the PR (e.g., risk:medium). Additionally, please consider adding screenshots of the new tab and filling the Contributors section (recommended). Once you add the risk:medium label and optionally include screenshots/contributor credits, this PR should be ready from a title/body/template compliance perspective. Thank you!
Last updated: Thu, 16 Apr 2026 02:14:57 GMT
Commit Type
Risk Level
What & Why
Adds a new "Agent Harness" tab to the node details panel in designer-v2 for consumption agent workflows. This tab allows users to view and configure the sandbox environment for agent code execution, including:
The tab is only visible when:
type === 'agent')enableAgentHarness: true(consumption only)Impact of Change
enableAgentHarnessproperty onOperationManifestProperties,getSandboxConfigurationsonIWorkflowService,integrationAccountonhostOptionsTest Plan
Contributors
Screenshots/Videos
🤖 Generated with Claude Code