chore: add workflows setting for agents CE (#40988)

/ok-to-test tags="@tag.Sanity"

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- **New Features**
- Added a "Trigger Settings" sidebar button and editor pane for AI Agent
apps.
- Introduced a "Run History" tab in the debugger and a corresponding
trigger in the bottom bar for AI Agent apps.
  - New SVG icon ("lightning-line") added for sidebar and UI elements.
- Support for importing agent templates and associating workflows with
applications.

- **Enhancements**
  - Sidebar and bottom bar now adapt to AI Agent app context.
- Layout and header updated to accommodate the new "Trigger Settings"
editor state.

- **Bug Fixes**
  - No bug fixes included in this release.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

<!-- This is an auto-generated comment: Cypress test results  -->
> [!TIP]
> 🟢 🟢 🟢 All cypress tests have passed! 🎉 🎉 🎉
> Workflow run:
<https://github.com/appsmithorg/appsmith/actions/runs/15753160141>
> Commit: d0e477ce71479ac091957566c5bba05e0f215e60
> <a
href="https://internal.appsmith.com/app/cypress-dashboard/rundetails-65890b3c81d7400d08fa9ee5?branch=master&workflowId=15753160141&attempt=2"
target="_blank">Cypress dashboard</a>.
> Tags: `@tag.Sanity`
> Spec:
> <hr>Thu, 19 Jun 2025 09:22:45 UTC
<!-- end of auto-generated comment: Cypress test results  -->
This commit is contained in:
Pawan Kumar 2025-06-19 18:44:22 +05:30 committed by GitHub
parent bb1c055126
commit f2b76c5577
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
27 changed files with 110 additions and 9 deletions

View File

@ -1213,6 +1213,10 @@ const AIChatIcon = importSvg(
async () => import("../__assets__/icons/ads/ai-chat.svg"),
);
const LightningLineIcon = importSvg(
async () => import("../__assets__/icons/ads/lightning-line.svg"),
);
import PlayIconPNG from "../__assets__/icons/control/play-icon.png";
function PlayIconPNGWrapper() {
@ -1636,6 +1640,7 @@ const ICON_LOOKUP = {
intercom: IntercomIcon,
onedrive: OnedriveIcon,
sharepoint: SharepointIcon,
"lightning-line": LightningLineIcon,
};
export const IconCollection = Object.keys(ICON_LOOKUP);

View File

@ -135,6 +135,7 @@ const Input = forwardRef<HTMLInputElement, InputProps>(
readOnly={isReadOnly}
ref={inputRef}
renderer={renderAs}
size="1"
value={value}
{...rest}
/>

View File

@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M13 9H21L11 24V15H4L13 0V9ZM11 11V7.22L7.532 13H13V17.394L17.263 11H11Z" fill="#4C5664"/>
</svg>

After

Width:  |  Height:  |  Size: 201 B

View File

@ -6,6 +6,7 @@ const SidebarButtonTitles = {
DATA: "Datasources",
SETTINGS: "Settings",
LIBRARIES: "Libraries",
TRIGGER_SETTINGS: "Trigger Settings",
};
export const EditorButton = (urlSuffix: string): IDESidebarButton => ({
@ -39,3 +40,11 @@ export const SettingsButton = (urlSuffix: string): IDESidebarButton => ({
testId: SidebarButtonTitles.SETTINGS,
urlSuffix,
});
export const TriggerSettingsButton = (urlSuffix: string): IDESidebarButton => ({
state: EditorState.TRIGGER_SETTINGS,
icon: "lightning-line",
tooltip: SidebarButtonTitles.TRIGGER_SETTINGS,
testId: SidebarButtonTitles.TRIGGER_SETTINGS,
urlSuffix,
});

View File

@ -9,4 +9,5 @@ export enum EditorState {
EDITOR = "EDITOR",
SETTINGS = "SETTINGS",
LIBRARIES = "LIBRARIES",
TRIGGER_SETTINGS = "TRIGGER_SETTINGS",
}

View File

@ -116,6 +116,18 @@ class TemplatesAPI extends Api {
body,
);
}
// This endpoint imports the application from a template and creates a workflow,
// and attaches the workflow to the application.
static async importAgentTemplate(
templateId: string,
workspaceId: string,
): Promise<AxiosPromise<ImportTemplateResponse>> {
return Api.post(TemplatesAPI.baseUrl + `/applications/agents`, {
templateId,
workspaceId,
});
}
}
export default TemplatesAPI;

View File

@ -64,6 +64,7 @@ export interface ApplicationResponsePayload {
slug: string;
applicationVersion: ApplicationVersion;
isPublic?: boolean;
connectedWorkflowId?: string;
}
export interface FetchApplicationPayload {

View File

@ -20,6 +20,7 @@ export interface Workflow {
// PR for reference: https://github.com/appsmithorg/appsmith/pull/8796
evaluationVersion: EvaluationVersion;
token?: string;
connectedApplicationId?: string;
}
export type WorkflowMetadata = Workflow;

View File

@ -69,6 +69,7 @@ export const DATA_SOURCES_EDITOR_ID_PATH = `/datasource/:datasourceId`;
export const APP_LIBRARIES_EDITOR_PATH = `/libraries`;
export const APP_PACKAGES_EDITOR_PATH = `/packages`;
export const APP_SETTINGS_EDITOR_PATH = `/settings`;
export const APP_TRIGGER_SETTINGS_EDITOR_PATH = `/trigger-settings`;
export const SAAS_GSHEET_EDITOR_ID_PATH = `/saas/google-sheets-plugin/datasources/:datasourceId`;
export const BUILDER_CHECKLIST_PATH = `/checklist`;
export const ADMIN_SETTINGS_PATH = "/settings";

View File

@ -3,6 +3,7 @@ import {
EditorButton,
LibrariesButton,
SettingsButton,
TriggerSettingsButton,
} from "IDE/constants/SidebarButtons";
import { Condition, type IDESidebarButton } from "@appsmith/ads";
import {
@ -18,9 +19,20 @@ const DataButtonWithWarning: IDESidebarButton = {
const DataButtonWithoutWarning = DataButton("datasource");
export const BottomButtons = (datasourcesExist: boolean) => [
datasourcesExist ? DataButtonWithoutWarning : DataButtonWithWarning,
LibrariesButton("libraries"),
SettingsButton("settings"),
];
export const BottomButtons = (
datasourcesExist: boolean,
isAgentApp: boolean,
) => {
const buttons = [
datasourcesExist ? DataButtonWithoutWarning : DataButtonWithWarning,
LibrariesButton("libraries"),
SettingsButton("settings"),
];
if (isAgentApp) {
buttons.splice(1, 0, TriggerSettingsButton("trigger-settings"));
}
return buttons;
};
export const TopButtons = [EditorButton("")];

View File

@ -6,6 +6,7 @@ import {
APP_LIBRARIES_EDITOR_PATH,
APP_PACKAGES_EDITOR_PATH,
APP_SETTINGS_EDITOR_PATH,
APP_TRIGGER_SETTINGS_EDITOR_PATH,
BUILDER_CHECKLIST_PATH,
BUILDER_CUSTOM_PATH,
BUILDER_PATH,
@ -71,6 +72,7 @@ export const MainPaneRoutes = (
`${path}${APP_LIBRARIES_EDITOR_PATH}`,
`${path}${APP_PACKAGES_EDITOR_PATH}`,
`${path}${APP_SETTINGS_EDITOR_PATH}`,
`${path}${APP_TRIGGER_SETTINGS_EDITOR_PATH}`,
],
},
{

View File

@ -19,6 +19,8 @@ import OldGitQuickActions from "pages/Editor/gitSync/QuickGitActions";
import { GitQuickActions } from "git";
import { useGitModEnabled } from "pages/Editor/gitSync/hooks/modHooks";
import { AIAgentSupportTrigger } from "ee/components/BottomBar/AIAgentSupportTrigger";
import { RunHistoryTrigger } from "ee/pages/WorkflowIDE/layouts/components/BottomBar/WorkflowRunHistory/RunHistoryTrigger";
import { getIsAiAgentApp } from "ee/selectors/aiAgentSelectors";
function GitActions() {
const isGitModEnabled = useGitModEnabled();
@ -34,6 +36,7 @@ export default function BottomBar() {
const isAnvilEnabled = useSelector(getIsAnvilEnabledInCurrentApplication);
const isPreviewMode = useSelector(previewModeSelector);
const isGitEnabled = !isAnvilEnabled && !isPreviewMode;
const isAiAgentApp = useSelector(getIsAiAgentApp);
const dispatch = useDispatch();
@ -67,6 +70,7 @@ export default function BottomBar() {
/>
</ManualUpgrades>
<AIAgentSupportTrigger />
{isAiAgentApp && <RunHistoryTrigger />}
<DebuggerTrigger />
<HelpButton />
</Wrapper>

View File

@ -27,6 +27,8 @@ import { StateInspector } from "./StateInspector";
import { getIDETypeByUrl } from "ee/entities/IDE/utils";
import { useLocation } from "react-router";
import { IDE_TYPE } from "ee/IDE/Interfaces/IDETypes";
import { RunHistoryTab } from "ee/pages/WorkflowIDE/layouts/components/BottomBar/WorkflowRunHistory/RunHistoryTab";
import { getIsAiAgentApp } from "ee/selectors/aiAgentSelectors";
function DebuggerTabs() {
const dispatch = useDispatch();
@ -42,6 +44,7 @@ function DebuggerTabs() {
},
[dispatch],
);
const isAiAgentApp = useSelector(getIsAiAgentApp);
const setSelectedTab = useCallback(
(tabKey: string) => {
@ -87,8 +90,16 @@ function DebuggerTabs() {
});
}
if (isAiAgentApp) {
tabs.push({
key: DEBUGGER_TAB_KEYS.RUN_HISTORY_TAB,
title: "Run History",
panelComponent: <RunHistoryTab />,
});
}
return tabs;
}, [errorCount, ideType]);
}, [errorCount, ideType, isAiAgentApp]);
// Do not render if response, header or schema tab is selected in the bottom bar.
const shouldRender = !(

View File

@ -7,4 +7,5 @@ export enum DEBUGGER_TAB_KEYS {
LOGS_TAB = "LOGS_TAB",
STATE_TAB = "STATE_TAB",
VISUALIZATION_TAB = "VISUALIZATION_TAB",
RUN_HISTORY_TAB = "RUN_HISTORY_TAB",
}

View File

@ -0,0 +1,3 @@
export const TriggerSettingsPane = () => {
return null;
};

View File

@ -0,0 +1 @@
export { TriggerSettingsPane } from "./TriggerSettingsPane";

View File

@ -0,0 +1,3 @@
export const RunHistoryTab = () => {
return null;
};

View File

@ -0,0 +1,3 @@
export const RunHistoryTrigger = () => {
return null;
};

View File

@ -0,0 +1 @@
export { RunHistoryTab } from "./RunHistoryTab";

View File

@ -0,0 +1 @@
export * from "./WorkflowRunHistory";

View File

@ -45,4 +45,5 @@ export interface ApplicationPayload {
isCommunityTemplate?: boolean;
publishedAppToCommunityTemplate?: boolean;
forkedFromTemplateTitle?: string;
connectedWorkflowId?: string;
}

View File

@ -32,6 +32,7 @@ export enum FocusEntity {
JS_MODULE_INSTANCE = "JS_MODULE_INSTANCE",
JS_OBJECT_ADD = "JS_OBJECT_ADD",
PAGE = "PAGE",
TRIGGER_SETTINGS = "TRIGGER_SETTINGS",
}
export const FocusStoreHierarchy: Partial<Record<FocusEntity, FocusEntity>> = {
@ -300,6 +301,15 @@ export function identifyEntityFromPath(path: string): FocusEntityInfo {
params: match.params,
};
}
if (match.params.entity === "trigger-settings") {
return {
entity: FocusEntity.TRIGGER_SETTINGS,
id: "",
appState: EditorState.TRIGGER_SETTINGS,
params: match.params,
};
}
}
return {

View File

@ -100,6 +100,8 @@ const HeaderTitleComponent = ({ appState }: HeaderTitleProps) => {
);
case EditorState.LIBRARIES:
return <IDEHeaderTitle key={appState} title={libraryHeaderTitle} />;
case EditorState.TRIGGER_SETTINGS:
return <IDEHeaderTitle key={appState} title="Trigger Settings" />;
default:
return <EditorTitle key={appState} />;
}

View File

@ -59,6 +59,7 @@ function useGridLayoutTemplate(): ReturnValue {
break;
case EditorState.SETTINGS:
case EditorState.TRIGGER_SETTINGS:
if (isPreviewMode || isProtectedMode) {
setColumns(["0px", "0px", `${windowWidth}px`, "0px"]);
} else {

View File

@ -5,6 +5,7 @@ import {
APP_LIBRARIES_EDITOR_PATH,
APP_PACKAGES_EDITOR_PATH,
APP_SETTINGS_EDITOR_PATH,
APP_TRIGGER_SETTINGS_EDITOR_PATH,
DATA_SOURCES_EDITOR_ID_PATH,
DATA_SOURCES_EDITOR_LIST_PATH,
INTEGRATION_EDITOR_PATH,
@ -13,6 +14,7 @@ import {
import AppSettingsPane from "../../components/AppSettings";
import { DataSidePane } from "pages/Editor/DataSidePane";
import EditorPane from "../components/EditorPane";
import { TriggerSettingsPane } from "ee/pages/AppIDE/components/TriggerSettingsPane";
import LibrarySidePane from "ee/pages/AppIDE/components/LibrariesList/LibrarySidePane";
import { getDatasourceUsageCountForApp } from "ee/selectors/entitiesSelector";
import { IDE_TYPE } from "ee/IDE/Interfaces/IDETypes";
@ -65,6 +67,11 @@ const LeftPane = () => {
exact
path={librarySidePanePaths}
/>
<SentryRoute
component={TriggerSettingsPane}
exact
path={`${path}${APP_TRIGGER_SETTINGS_EDITOR_PATH}`}
/>
<SentryRoute
component={AppSettingsPane}
exact

View File

@ -12,6 +12,7 @@ import {
BottomButtons,
TopButtons,
} from "ee/pages/AppIDE/constants/SidebarButtons";
import { getIsAiAgentApp } from "ee/selectors/aiAgentSelectors";
function Sidebar() {
const dispatch = useDispatch();
@ -20,11 +21,12 @@ function Sidebar() {
const currentWorkspaceId = useSelector(getCurrentWorkspaceId);
const datasources = useSelector(getDatasources);
const datasourcesExist = datasources.length > 0;
const isAgentApp = useSelector(getIsAiAgentApp);
// Updates the bottom button config based on datasource existence
const bottomButtons = React.useMemo(() => {
return BottomButtons(datasourcesExist);
}, [datasourcesExist]);
return BottomButtons(datasourcesExist, isAgentApp);
}, [datasourcesExist, isAgentApp]);
useEffect(() => {
dispatch(fetchWorkspace(currentWorkspaceId));

View File

@ -89,7 +89,9 @@ function* importTemplateToWorkspaceSaga(
try {
const response: ImportTemplateResponse = yield call(
TemplatesAPI.importTemplate,
isAgentTemplate
? TemplatesAPI.importAgentTemplate
: TemplatesAPI.importTemplate,
action.payload.templateId,
action.payload.workspaceId,
);