diff --git a/.github/workflows/platform-frontend-ci.yml b/.github/workflows/platform-frontend-ci.yml
index d0edc7327d..fb7a55055e 100644
--- a/.github/workflows/platform-frontend-ci.yml
+++ b/.github/workflows/platform-frontend-ci.yml
@@ -235,13 +235,25 @@ jobs:
- name: Run Playwright tests
run: pnpm test:no-build
+ continue-on-error: false
- - name: Upload Playwright artifacts
- if: failure()
+ - name: Upload Playwright report
+ if: always()
uses: actions/upload-artifact@v4
with:
name: playwright-report
path: playwright-report
+ if-no-files-found: ignore
+ retention-days: 3
+
+ - name: Upload Playwright test results
+ if: always()
+ uses: actions/upload-artifact@v4
+ with:
+ name: playwright-test-results
+ path: test-results
+ if-no-files-found: ignore
+ retention-days: 3
- name: Print Final Docker Compose logs
if: always()
diff --git a/autogpt_platform/backend/backend/api/features/integrations/router.py b/autogpt_platform/backend/backend/api/features/integrations/router.py
index 36585b14b5..00500dc8a8 100644
--- a/autogpt_platform/backend/backend/api/features/integrations/router.py
+++ b/autogpt_platform/backend/backend/api/features/integrations/router.py
@@ -171,6 +171,7 @@ async def callback(
f"Successfully processed OAuth callback for user {user_id} "
f"and provider {provider.value}"
)
+
return CredentialsMetaResponse(
id=credentials.id,
provider=credentials.provider,
@@ -189,6 +190,7 @@ async def list_credentials(
user_id: Annotated[str, Security(get_user_id)],
) -> list[CredentialsMetaResponse]:
credentials = await creds_manager.store.get_all_creds(user_id)
+
return [
CredentialsMetaResponse(
id=cred.id,
@@ -211,6 +213,7 @@ async def list_credentials_by_provider(
user_id: Annotated[str, Security(get_user_id)],
) -> list[CredentialsMetaResponse]:
credentials = await creds_manager.store.get_creds_by_provider(user_id, provider)
+
return [
CredentialsMetaResponse(
id=cred.id,
@@ -826,6 +829,18 @@ async def list_providers() -> List[str]:
return all_providers
+@router.get("/providers/system", response_model=List[str])
+async def list_system_providers() -> List[str]:
+ """
+ Get a list of providers that have platform credits (system credentials) available.
+
+ These providers can be used without the user providing their own API keys.
+ """
+ from backend.integrations.credentials_store import SYSTEM_PROVIDERS
+
+ return list(SYSTEM_PROVIDERS)
+
+
@router.get("/providers/names", response_model=ProviderNamesResponse)
async def get_provider_names() -> ProviderNamesResponse:
"""
diff --git a/autogpt_platform/backend/backend/integrations/credentials_store.py b/autogpt_platform/backend/backend/integrations/credentials_store.py
index 7d805913b2..68fa16b38d 100644
--- a/autogpt_platform/backend/backend/integrations/credentials_store.py
+++ b/autogpt_platform/backend/backend/integrations/credentials_store.py
@@ -245,6 +245,21 @@ DEFAULT_CREDENTIALS = [
webshare_proxy_credentials,
]
+SYSTEM_CREDENTIAL_IDS = {cred.id for cred in DEFAULT_CREDENTIALS}
+
+# Set of providers that have system credentials available
+SYSTEM_PROVIDERS = {cred.provider for cred in DEFAULT_CREDENTIALS}
+
+
+def is_system_credential(credential_id: str) -> bool:
+ """Check if a credential ID belongs to a system-managed credential."""
+ return credential_id in SYSTEM_CREDENTIAL_IDS
+
+
+def is_system_provider(provider: str) -> bool:
+ """Check if a provider has system-managed credentials available."""
+ return provider in SYSTEM_PROVIDERS
+
class IntegrationCredentialsStore:
def __init__(self):
diff --git a/autogpt_platform/frontend/CONTRIBUTING.md b/autogpt_platform/frontend/CONTRIBUTING.md
index 048c088350..1b2b810986 100644
--- a/autogpt_platform/frontend/CONTRIBUTING.md
+++ b/autogpt_platform/frontend/CONTRIBUTING.md
@@ -708,10 +708,7 @@ export function CreateButton() {
## π§ͺ Testing & Storybook
-- End-to-end: [Playwright](https://playwright.dev/docs/intro) (`pnpm test`, `pnpm test-ui`)
-- [Storybook](https://storybook.js.org/docs) for isolated UI development (`pnpm storybook` / `pnpm build-storybook`)
-- For Storybook tests in CI, see [`@storybook/test-runner`](https://storybook.js.org/docs/writing-tests/test-runner) (`test-storybook:ci`)
-- When changing components in `src/components`, update or add stories and visually verify in Storybook/Chromatic
+- See `TESTING.md` for Playwright setup, E2E data seeding, and Storybook usage.
---
diff --git a/autogpt_platform/frontend/README.md b/autogpt_platform/frontend/README.md
index f4541cdd33..abea810fd2 100644
--- a/autogpt_platform/frontend/README.md
+++ b/autogpt_platform/frontend/README.md
@@ -5,6 +5,7 @@ This is the frontend for AutoGPT's next generation
This project uses [**pnpm**](https://pnpm.io/) as the package manager via **corepack**. [Corepack](https://github.com/nodejs/corepack) is a Node.js tool that automatically manages package managers without requiring global installations.
For architecture, conventions, data fetching, feature flags, design system usage, state management, and PR process, see [CONTRIBUTING.md](./CONTRIBUTING.md).
+For Playwright and Storybook testing setup, see [TESTING.md](./TESTING.md).
### Prerequisites
diff --git a/autogpt_platform/frontend/TESTING.md b/autogpt_platform/frontend/TESTING.md
new file mode 100644
index 0000000000..2995295c96
--- /dev/null
+++ b/autogpt_platform/frontend/TESTING.md
@@ -0,0 +1,57 @@
+# Frontend Testing π§ͺ
+
+## Quick Start (local) π
+
+1. Start the backend + Supabase stack:
+ - From `autogpt_platform`: `docker compose --profile local up deps_backend -d`
+ - Or run the full stack: `docker compose up -d`
+2. Seed rich E2E data (creates `test123@gmail.com` with library agents):
+ - From `autogpt_platform/backend`: `poetry run python test/e2e_test_data.py`
+3. Run Playwright:
+ - From `autogpt_platform/frontend`: `pnpm test` or `pnpm test-ui`
+
+## How Playwright setup works π
+
+- Playwright runs from `frontend/playwright.config.ts` with a global setup step.
+- The global setup creates a user pool via the real signup UI and stores it in `frontend/.auth/user-pool.json`.
+- Most tests call `getTestUser()` (from `src/tests/utils/auth.ts`) which pulls a random user from that pool.
+ - these users do not contain library agents, it's user that just "signed up" on the platform, hence some tests to make use of users created via script (see below) with more data
+
+## Test users π€
+
+- **User pool (basic users)**
+ Created automatically by the Playwright global setup through `/signup`.
+ Used by `getTestUser()` in `src/tests/utils/auth.ts`.
+
+- **Rich user with library agents**
+ Created by `backend/test/e2e_test_data.py`.
+ Accessed via `getTestUserWithLibraryAgents()` in `src/tests/credentials/index.ts`.
+
+Use the rich user when a test needs existing library agents (e.g. `library.spec.ts`).
+
+## Resetting or wiping the DB π
+
+If you reset the Docker DB and logins start failing:
+
+1. Delete `frontend/.auth/user-pool.json` so the pool is regenerated.
+2. Re-run the E2E data script to recreate the rich user + library agents:
+ - `poetry run python test/e2e_test_data.py`
+
+## Storybook π
+
+## Flow diagram πΊοΈ
+
+```mermaid
+flowchart TD
+ A[Start Docker stack] --> B[Run e2e_test_data.py]
+ B --> C[Run Playwright tests]
+ C --> D[Global setup creates user pool]
+ D --> E{Test needs rich data?}
+ E -->|No| F[getTestUser from user pool]
+ E -->|Yes| G[getTestUserWithLibraryAgents]
+```
+
+- `pnpm storybook` β Run Storybook locally
+- `pnpm build-storybook` β Build a static Storybook
+- CI runner: `pnpm test-storybook`
+- When changing components in `src/components`, update or add stories and verify in Storybook/Chromatic.
diff --git a/autogpt_platform/frontend/next.config.mjs b/autogpt_platform/frontend/next.config.mjs
index e4e4cdf544..bb4410039d 100644
--- a/autogpt_platform/frontend/next.config.mjs
+++ b/autogpt_platform/frontend/next.config.mjs
@@ -3,6 +3,13 @@ import { withSentryConfig } from "@sentry/nextjs";
/** @type {import('next').NextConfig} */
const nextConfig = {
productionBrowserSourceMaps: true,
+ // Externalize OpenTelemetry packages to fix Turbopack HMR issues
+ serverExternalPackages: [
+ "@opentelemetry/instrumentation",
+ "@opentelemetry/sdk-node",
+ "import-in-the-middle",
+ "require-in-the-middle",
+ ],
experimental: {
serverActions: {
bodySizeLimit: "256mb",
diff --git a/autogpt_platform/frontend/package.json b/autogpt_platform/frontend/package.json
index f881ebaf5b..0823400c87 100644
--- a/autogpt_platform/frontend/package.json
+++ b/autogpt_platform/frontend/package.json
@@ -32,6 +32,7 @@
"@hookform/resolvers": "5.2.2",
"@next/third-parties": "15.4.6",
"@phosphor-icons/react": "2.1.10",
+ "@radix-ui/react-accordion": "1.2.12",
"@radix-ui/react-alert-dialog": "1.1.15",
"@radix-ui/react-avatar": "1.1.10",
"@radix-ui/react-checkbox": "1.3.3",
@@ -117,6 +118,7 @@
},
"devDependencies": {
"@chromatic-com/storybook": "4.1.2",
+ "@opentelemetry/instrumentation": "0.209.0",
"@playwright/test": "1.56.1",
"@storybook/addon-a11y": "9.1.5",
"@storybook/addon-docs": "9.1.5",
@@ -140,6 +142,7 @@
"eslint": "8.57.1",
"eslint-config-next": "15.5.7",
"eslint-plugin-storybook": "9.1.5",
+ "import-in-the-middle": "2.0.2",
"msw": "2.11.6",
"msw-storybook-addon": "2.0.6",
"orval": "7.13.0",
@@ -147,7 +150,7 @@
"postcss": "8.5.6",
"prettier": "3.6.2",
"prettier-plugin-tailwindcss": "0.7.1",
- "require-in-the-middle": "7.5.2",
+ "require-in-the-middle": "8.0.1",
"storybook": "9.1.5",
"tailwindcss": "3.4.17",
"typescript": "5.9.3"
@@ -157,5 +160,10 @@
"public"
]
},
+ "pnpm": {
+ "overrides": {
+ "@opentelemetry/instrumentation": "0.209.0"
+ }
+ },
"packageManager": "pnpm@10.20.0+sha512.cf9998222162dd85864d0a8102e7892e7ba4ceadebbf5a31f9c2fce48dfce317a9c53b9f6464d1ef9042cba2e02ae02a9f7c143a2b438cd93c91840f0192b9dd"
}
diff --git a/autogpt_platform/frontend/pnpm-lock.yaml b/autogpt_platform/frontend/pnpm-lock.yaml
index 4240d0d155..f503c23ea8 100644
--- a/autogpt_platform/frontend/pnpm-lock.yaml
+++ b/autogpt_platform/frontend/pnpm-lock.yaml
@@ -4,6 +4,9 @@ settings:
autoInstallPeers: true
excludeLinksFromLockfile: false
+overrides:
+ '@opentelemetry/instrumentation': 0.209.0
+
importers:
.:
@@ -20,6 +23,9 @@ importers:
'@phosphor-icons/react':
specifier: 2.1.10
version: 2.1.10(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@radix-ui/react-accordion':
+ specifier: 1.2.12
+ version: 1.2.12(@types/react-dom@18.3.5(@types/react@18.3.17))(@types/react@18.3.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@radix-ui/react-alert-dialog':
specifier: 1.1.15
version: 1.1.15(@types/react-dom@18.3.5(@types/react@18.3.17))(@types/react@18.3.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
@@ -270,6 +276,9 @@ importers:
'@chromatic-com/storybook':
specifier: 4.1.2
version: 4.1.2(storybook@9.1.5(@testing-library/dom@10.4.1)(msw@2.11.6(@types/node@24.10.0)(typescript@5.9.3))(prettier@3.6.2))
+ '@opentelemetry/instrumentation':
+ specifier: 0.209.0
+ version: 0.209.0(@opentelemetry/api@1.9.0)
'@playwright/test':
specifier: 1.56.1
version: 1.56.1
@@ -339,6 +348,9 @@ importers:
eslint-plugin-storybook:
specifier: 9.1.5
version: 9.1.5(eslint@8.57.1)(storybook@9.1.5(@testing-library/dom@10.4.1)(msw@2.11.6(@types/node@24.10.0)(typescript@5.9.3))(prettier@3.6.2))(typescript@5.9.3)
+ import-in-the-middle:
+ specifier: 2.0.2
+ version: 2.0.2
msw:
specifier: 2.11.6
version: 2.11.6(@types/node@24.10.0)(typescript@5.9.3)
@@ -361,8 +373,8 @@ importers:
specifier: 0.7.1
version: 0.7.1(prettier@3.6.2)
require-in-the-middle:
- specifier: 7.5.2
- version: 7.5.2
+ specifier: 8.0.1
+ version: 8.0.1
storybook:
specifier: 9.1.5
version: 9.1.5(@testing-library/dom@10.4.1)(msw@2.11.6(@types/node@24.10.0)(typescript@5.9.3))(prettier@3.6.2)
@@ -1543,8 +1555,8 @@ packages:
'@open-draft/until@2.1.0':
resolution: {integrity: sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg==}
- '@opentelemetry/api-logs@0.208.0':
- resolution: {integrity: sha512-CjruKY9V6NMssL/T1kAFgzosF1v9o6oeN+aX5JB/C/xPNtmgIJqcXHG7fA82Ou1zCpWGl4lROQUKwUNE1pMCyg==}
+ '@opentelemetry/api-logs@0.209.0':
+ resolution: {integrity: sha512-xomnUNi7TiAGtOgs0tb54LyrjRZLu9shJGGwkcN7NgtiPYOpNnKLkRJtzZvTjD/w6knSZH9sFZcUSUovYOPg6A==}
engines: {node: '>=8.0.0'}
'@opentelemetry/api@1.9.0':
@@ -1695,8 +1707,8 @@ packages:
peerDependencies:
'@opentelemetry/api': ^1.7.0
- '@opentelemetry/instrumentation@0.208.0':
- resolution: {integrity: sha512-Eju0L4qWcQS+oXxi6pgh7zvE2byogAkcsVv0OjHF/97iOz1N/aKE6etSGowYkie+YA1uo6DNwdSxaaNnLvcRlA==}
+ '@opentelemetry/instrumentation@0.209.0':
+ resolution: {integrity: sha512-Cwe863ojTCnFlxVuuhG7s6ODkAOzKsAEthKAcI4MDRYz1OmGWYnmSl4X2pbyS+hBxVTdvfZePfoEA01IjqcEyw==}
engines: {node: ^18.19.0 || >=20.6.0}
peerDependencies:
'@opentelemetry/api': ^1.3.0
@@ -1810,6 +1822,19 @@ packages:
'@radix-ui/primitive@1.1.3':
resolution: {integrity: sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==}
+ '@radix-ui/react-accordion@1.2.12':
+ resolution: {integrity: sha512-T4nygeh9YE9dLRPhAHSeOZi7HBXo+0kYIPJXayZfvWOWA0+n3dESrZbjfDPUABkUNym6Hd+f2IR113To8D2GPA==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
'@radix-ui/react-alert-dialog@1.1.15':
resolution: {integrity: sha512-oTVLkEw5GpdRe29BqJ0LSDFWI3qu0vR1M0mUkOQWDIUnY/QIkLpgDMWuKxP94c2NAC2LGcgVhG1ImF3jkZ5wXw==}
peerDependencies:
@@ -2631,7 +2656,7 @@ packages:
'@opentelemetry/api': ^1.9.0
'@opentelemetry/context-async-hooks': ^1.30.1 || ^2.1.0 || ^2.2.0
'@opentelemetry/core': ^1.30.1 || ^2.1.0 || ^2.2.0
- '@opentelemetry/instrumentation': '>=0.57.1 <1'
+ '@opentelemetry/instrumentation': 0.209.0
'@opentelemetry/resources': ^1.30.1 || ^2.1.0 || ^2.2.0
'@opentelemetry/sdk-trace-base': ^1.30.1 || ^2.1.0 || ^2.2.0
'@opentelemetry/semantic-conventions': ^1.37.0
@@ -4957,8 +4982,8 @@ packages:
resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==}
engines: {node: '>=6'}
- import-in-the-middle@2.0.1:
- resolution: {integrity: sha512-bruMpJ7xz+9jwGzrwEhWgvRrlKRYCRDBrfU+ur3FcasYXLJDxTruJ//8g2Noj+QFyRBeqbpj8Bhn4Fbw6HjvhA==}
+ import-in-the-middle@2.0.2:
+ resolution: {integrity: sha512-qet/hkGt3EbNGVtbDfPu0BM+tCqBS8wT1SYrstPaDKoWtshsC6licOemz7DVtpBEyvDNzo8UTBf9/GwWuSDZ9w==}
imurmurhash@0.1.4:
resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==}
@@ -6502,10 +6527,6 @@ packages:
resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==}
engines: {node: '>=0.10.0'}
- require-in-the-middle@7.5.2:
- resolution: {integrity: sha512-gAZ+kLqBdHarXB64XpAe2VCjB7rIRv+mU8tfRWziHRJ5umKsIHN2tLLv6EtMw7WCdP19S0ERVMldNvxYCHnhSQ==}
- engines: {node: '>=8.6.0'}
-
require-in-the-middle@8.0.1:
resolution: {integrity: sha512-QT7FVMXfWOYFbeRBF6nu+I6tr2Tf3u0q8RIEjNob/heKY/nh7drD/k7eeMFmSQgnTtCzLDcCu/XEnpW2wk4xCQ==}
engines: {node: '>=9.3.0 || >=8.10.0 <9.0.0'}
@@ -8716,7 +8737,7 @@ snapshots:
'@open-draft/until@2.1.0': {}
- '@opentelemetry/api-logs@0.208.0':
+ '@opentelemetry/api-logs@0.209.0':
dependencies:
'@opentelemetry/api': 1.9.0
@@ -8735,7 +8756,7 @@ snapshots:
dependencies:
'@opentelemetry/api': 1.9.0
'@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0)
- '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation': 0.209.0(@opentelemetry/api@1.9.0)
transitivePeerDependencies:
- supports-color
@@ -8743,7 +8764,7 @@ snapshots:
dependencies:
'@opentelemetry/api': 1.9.0
'@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0)
- '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation': 0.209.0(@opentelemetry/api@1.9.0)
'@opentelemetry/semantic-conventions': 1.38.0
'@types/connect': 3.4.38
transitivePeerDependencies:
@@ -8752,7 +8773,7 @@ snapshots:
'@opentelemetry/instrumentation-dataloader@0.26.0(@opentelemetry/api@1.9.0)':
dependencies:
'@opentelemetry/api': 1.9.0
- '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation': 0.209.0(@opentelemetry/api@1.9.0)
transitivePeerDependencies:
- supports-color
@@ -8760,7 +8781,7 @@ snapshots:
dependencies:
'@opentelemetry/api': 1.9.0
'@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0)
- '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation': 0.209.0(@opentelemetry/api@1.9.0)
'@opentelemetry/semantic-conventions': 1.38.0
transitivePeerDependencies:
- supports-color
@@ -8769,21 +8790,21 @@ snapshots:
dependencies:
'@opentelemetry/api': 1.9.0
'@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0)
- '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation': 0.209.0(@opentelemetry/api@1.9.0)
transitivePeerDependencies:
- supports-color
'@opentelemetry/instrumentation-generic-pool@0.52.0(@opentelemetry/api@1.9.0)':
dependencies:
'@opentelemetry/api': 1.9.0
- '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation': 0.209.0(@opentelemetry/api@1.9.0)
transitivePeerDependencies:
- supports-color
'@opentelemetry/instrumentation-graphql@0.56.0(@opentelemetry/api@1.9.0)':
dependencies:
'@opentelemetry/api': 1.9.0
- '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation': 0.209.0(@opentelemetry/api@1.9.0)
transitivePeerDependencies:
- supports-color
@@ -8791,7 +8812,7 @@ snapshots:
dependencies:
'@opentelemetry/api': 1.9.0
'@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0)
- '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation': 0.209.0(@opentelemetry/api@1.9.0)
'@opentelemetry/semantic-conventions': 1.38.0
transitivePeerDependencies:
- supports-color
@@ -8800,7 +8821,7 @@ snapshots:
dependencies:
'@opentelemetry/api': 1.9.0
'@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0)
- '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation': 0.209.0(@opentelemetry/api@1.9.0)
'@opentelemetry/semantic-conventions': 1.38.0
forwarded-parse: 2.1.2
transitivePeerDependencies:
@@ -8809,7 +8830,7 @@ snapshots:
'@opentelemetry/instrumentation-ioredis@0.56.0(@opentelemetry/api@1.9.0)':
dependencies:
'@opentelemetry/api': 1.9.0
- '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation': 0.209.0(@opentelemetry/api@1.9.0)
'@opentelemetry/redis-common': 0.38.2
transitivePeerDependencies:
- supports-color
@@ -8817,7 +8838,7 @@ snapshots:
'@opentelemetry/instrumentation-kafkajs@0.18.0(@opentelemetry/api@1.9.0)':
dependencies:
'@opentelemetry/api': 1.9.0
- '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation': 0.209.0(@opentelemetry/api@1.9.0)
'@opentelemetry/semantic-conventions': 1.38.0
transitivePeerDependencies:
- supports-color
@@ -8825,7 +8846,7 @@ snapshots:
'@opentelemetry/instrumentation-knex@0.53.0(@opentelemetry/api@1.9.0)':
dependencies:
'@opentelemetry/api': 1.9.0
- '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation': 0.209.0(@opentelemetry/api@1.9.0)
'@opentelemetry/semantic-conventions': 1.38.0
transitivePeerDependencies:
- supports-color
@@ -8834,7 +8855,7 @@ snapshots:
dependencies:
'@opentelemetry/api': 1.9.0
'@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0)
- '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation': 0.209.0(@opentelemetry/api@1.9.0)
'@opentelemetry/semantic-conventions': 1.38.0
transitivePeerDependencies:
- supports-color
@@ -8842,14 +8863,14 @@ snapshots:
'@opentelemetry/instrumentation-lru-memoizer@0.53.0(@opentelemetry/api@1.9.0)':
dependencies:
'@opentelemetry/api': 1.9.0
- '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation': 0.209.0(@opentelemetry/api@1.9.0)
transitivePeerDependencies:
- supports-color
'@opentelemetry/instrumentation-mongodb@0.61.0(@opentelemetry/api@1.9.0)':
dependencies:
'@opentelemetry/api': 1.9.0
- '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation': 0.209.0(@opentelemetry/api@1.9.0)
transitivePeerDependencies:
- supports-color
@@ -8857,14 +8878,14 @@ snapshots:
dependencies:
'@opentelemetry/api': 1.9.0
'@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0)
- '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation': 0.209.0(@opentelemetry/api@1.9.0)
transitivePeerDependencies:
- supports-color
'@opentelemetry/instrumentation-mysql2@0.55.0(@opentelemetry/api@1.9.0)':
dependencies:
'@opentelemetry/api': 1.9.0
- '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation': 0.209.0(@opentelemetry/api@1.9.0)
'@opentelemetry/semantic-conventions': 1.38.0
'@opentelemetry/sql-common': 0.41.2(@opentelemetry/api@1.9.0)
transitivePeerDependencies:
@@ -8873,7 +8894,7 @@ snapshots:
'@opentelemetry/instrumentation-mysql@0.54.0(@opentelemetry/api@1.9.0)':
dependencies:
'@opentelemetry/api': 1.9.0
- '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation': 0.209.0(@opentelemetry/api@1.9.0)
'@types/mysql': 2.15.27
transitivePeerDependencies:
- supports-color
@@ -8882,7 +8903,7 @@ snapshots:
dependencies:
'@opentelemetry/api': 1.9.0
'@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0)
- '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation': 0.209.0(@opentelemetry/api@1.9.0)
'@opentelemetry/semantic-conventions': 1.38.0
'@opentelemetry/sql-common': 0.41.2(@opentelemetry/api@1.9.0)
'@types/pg': 8.15.6
@@ -8893,7 +8914,7 @@ snapshots:
'@opentelemetry/instrumentation-redis@0.57.0(@opentelemetry/api@1.9.0)':
dependencies:
'@opentelemetry/api': 1.9.0
- '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation': 0.209.0(@opentelemetry/api@1.9.0)
'@opentelemetry/redis-common': 0.38.2
'@opentelemetry/semantic-conventions': 1.38.0
transitivePeerDependencies:
@@ -8902,7 +8923,7 @@ snapshots:
'@opentelemetry/instrumentation-tedious@0.27.0(@opentelemetry/api@1.9.0)':
dependencies:
'@opentelemetry/api': 1.9.0
- '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation': 0.209.0(@opentelemetry/api@1.9.0)
'@types/tedious': 4.0.14
transitivePeerDependencies:
- supports-color
@@ -8911,16 +8932,16 @@ snapshots:
dependencies:
'@opentelemetry/api': 1.9.0
'@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0)
- '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation': 0.209.0(@opentelemetry/api@1.9.0)
'@opentelemetry/semantic-conventions': 1.38.0
transitivePeerDependencies:
- supports-color
- '@opentelemetry/instrumentation@0.208.0(@opentelemetry/api@1.9.0)':
+ '@opentelemetry/instrumentation@0.209.0(@opentelemetry/api@1.9.0)':
dependencies:
'@opentelemetry/api': 1.9.0
- '@opentelemetry/api-logs': 0.208.0
- import-in-the-middle: 2.0.1
+ '@opentelemetry/api-logs': 0.209.0
+ import-in-the-middle: 2.0.2
require-in-the-middle: 8.0.1
transitivePeerDependencies:
- supports-color
@@ -9100,7 +9121,7 @@ snapshots:
'@prisma/instrumentation@6.19.0(@opentelemetry/api@1.9.0)':
dependencies:
'@opentelemetry/api': 1.9.0
- '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation': 0.209.0(@opentelemetry/api@1.9.0)
transitivePeerDependencies:
- supports-color
@@ -9108,6 +9129,23 @@ snapshots:
'@radix-ui/primitive@1.1.3': {}
+ '@radix-ui/react-accordion@1.2.12(@types/react-dom@18.3.5(@types/react@18.3.17))(@types/react@18.3.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.3
+ '@radix-ui/react-collapsible': 1.1.12(@types/react-dom@18.3.5(@types/react@18.3.17))(@types/react@18.3.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@radix-ui/react-collection': 1.1.7(@types/react-dom@18.3.5(@types/react@18.3.17))(@types/react@18.3.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.17)(react@18.3.1)
+ '@radix-ui/react-context': 1.1.2(@types/react@18.3.17)(react@18.3.1)
+ '@radix-ui/react-direction': 1.1.1(@types/react@18.3.17)(react@18.3.1)
+ '@radix-ui/react-id': 1.1.1(@types/react@18.3.17)(react@18.3.1)
+ '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.5(@types/react@18.3.17))(@types/react@18.3.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@18.3.17)(react@18.3.1)
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+ optionalDependencies:
+ '@types/react': 18.3.17
+ '@types/react-dom': 18.3.5(@types/react@18.3.17)
+
'@radix-ui/react-alert-dialog@1.1.15(@types/react-dom@18.3.5(@types/react@18.3.17))(@types/react@18.3.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies:
'@radix-ui/primitive': 1.1.3
@@ -9932,19 +9970,19 @@ snapshots:
- supports-color
- webpack
- '@sentry/node-core@10.27.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.208.0(@opentelemetry/api@1.9.0))(@opentelemetry/resources@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.38.0)':
+ '@sentry/node-core@10.27.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.209.0(@opentelemetry/api@1.9.0))(@opentelemetry/resources@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.38.0)':
dependencies:
'@apm-js-collab/tracing-hooks': 0.3.1
'@opentelemetry/api': 1.9.0
'@opentelemetry/context-async-hooks': 2.2.0(@opentelemetry/api@1.9.0)
'@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0)
- '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation': 0.209.0(@opentelemetry/api@1.9.0)
'@opentelemetry/resources': 2.2.0(@opentelemetry/api@1.9.0)
'@opentelemetry/sdk-trace-base': 2.2.0(@opentelemetry/api@1.9.0)
'@opentelemetry/semantic-conventions': 1.38.0
'@sentry/core': 10.27.0
'@sentry/opentelemetry': 10.27.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.38.0)
- import-in-the-middle: 2.0.1
+ import-in-the-middle: 2.0.2
transitivePeerDependencies:
- supports-color
@@ -9953,7 +9991,7 @@ snapshots:
'@opentelemetry/api': 1.9.0
'@opentelemetry/context-async-hooks': 2.2.0(@opentelemetry/api@1.9.0)
'@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0)
- '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation': 0.209.0(@opentelemetry/api@1.9.0)
'@opentelemetry/instrumentation-amqplib': 0.55.0(@opentelemetry/api@1.9.0)
'@opentelemetry/instrumentation-connect': 0.52.0(@opentelemetry/api@1.9.0)
'@opentelemetry/instrumentation-dataloader': 0.26.0(@opentelemetry/api@1.9.0)
@@ -9981,9 +10019,9 @@ snapshots:
'@opentelemetry/semantic-conventions': 1.38.0
'@prisma/instrumentation': 6.19.0(@opentelemetry/api@1.9.0)
'@sentry/core': 10.27.0
- '@sentry/node-core': 10.27.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.208.0(@opentelemetry/api@1.9.0))(@opentelemetry/resources@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.38.0)
+ '@sentry/node-core': 10.27.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.209.0(@opentelemetry/api@1.9.0))(@opentelemetry/resources@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.38.0)
'@sentry/opentelemetry': 10.27.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.38.0)
- import-in-the-middle: 2.0.1
+ import-in-the-middle: 2.0.2
minimatch: 9.0.5
transitivePeerDependencies:
- supports-color
@@ -12792,7 +12830,7 @@ snapshots:
parent-module: 1.0.1
resolve-from: 4.0.0
- import-in-the-middle@2.0.1:
+ import-in-the-middle@2.0.2:
dependencies:
acorn: 8.15.0
acorn-import-attributes: 1.9.5(acorn@8.15.0)
@@ -14631,14 +14669,6 @@ snapshots:
require-from-string@2.0.2: {}
- require-in-the-middle@7.5.2:
- dependencies:
- debug: 4.4.3
- module-details-from-path: 1.0.4
- resolve: 1.22.11
- transitivePeerDependencies:
- - supports-color
-
require-in-the-middle@8.0.1:
dependencies:
debug: 4.4.3
diff --git a/autogpt_platform/frontend/src/app/(no-navbar)/onboarding/5-run/components/AgentOnboardingCredentials/AgentOnboardingCredentials.tsx b/autogpt_platform/frontend/src/app/(no-navbar)/onboarding/5-run/components/AgentOnboardingCredentials/AgentOnboardingCredentials.tsx
index 3176ec7f70..72e296fd88 100644
--- a/autogpt_platform/frontend/src/app/(no-navbar)/onboarding/5-run/components/AgentOnboardingCredentials/AgentOnboardingCredentials.tsx
+++ b/autogpt_platform/frontend/src/app/(no-navbar)/onboarding/5-run/components/AgentOnboardingCredentials/AgentOnboardingCredentials.tsx
@@ -1,4 +1,4 @@
-import { CredentialsInput } from "@/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/modals/CredentialsInputs/CredentialsInputs";
+import { CredentialsInput } from "@/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/modals/CredentialsInputs/CredentialsInput";
import { CredentialsMetaInput } from "@/app/api/__generated__/models/credentialsMetaInput";
import { GraphMeta } from "@/app/api/__generated__/models/graphMeta";
import { useState } from "react";
diff --git a/autogpt_platform/frontend/src/app/(platform)/auth/integrations/setup-wizard/page.tsx b/autogpt_platform/frontend/src/app/(platform)/auth/integrations/setup-wizard/page.tsx
index 3372772c89..f7d4935907 100644
--- a/autogpt_platform/frontend/src/app/(platform)/auth/integrations/setup-wizard/page.tsx
+++ b/autogpt_platform/frontend/src/app/(platform)/auth/integrations/setup-wizard/page.tsx
@@ -1,22 +1,22 @@
"use client";
-import Image from "next/image";
-import Link from "next/link";
-import { useSearchParams } from "next/navigation";
-import { useState, useMemo, useRef } from "react";
-import { AuthCard } from "@/components/auth/AuthCard";
-import { Text } from "@/components/atoms/Text/Text";
+import { CredentialsInput } from "@/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/modals/CredentialsInputs/CredentialsInput";
+import { useGetOauthGetOauthAppInfo } from "@/app/api/__generated__/endpoints/oauth/oauth";
+import { okData } from "@/app/api/helpers";
import { Button } from "@/components/atoms/Button/Button";
+import { Text } from "@/components/atoms/Text/Text";
+import { AuthCard } from "@/components/auth/AuthCard";
import { ErrorCard } from "@/components/molecules/ErrorCard/ErrorCard";
-import { CredentialsInput } from "@/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/modals/CredentialsInputs/CredentialsInputs";
import type {
BlockIOCredentialsSubSchema,
CredentialsMetaInput,
CredentialsType,
} from "@/lib/autogpt-server-api";
import { CheckIcon, CircleIcon } from "@phosphor-icons/react";
-import { useGetOauthGetOauthAppInfo } from "@/app/api/__generated__/endpoints/oauth/oauth";
-import { okData } from "@/app/api/helpers";
+import Image from "next/image";
+import Link from "next/link";
+import { useSearchParams } from "next/navigation";
+import { useMemo, useRef, useState } from "react";
// All credential types - we accept any type of credential
const ALL_CREDENTIAL_TYPES: CredentialsType[] = [
diff --git a/autogpt_platform/frontend/src/app/(platform)/build/components/legacy-builder/NodeInputs.tsx b/autogpt_platform/frontend/src/app/(platform)/build/components/legacy-builder/NodeInputs.tsx
index ab6ea8b94b..36df180c8c 100644
--- a/autogpt_platform/frontend/src/app/(platform)/build/components/legacy-builder/NodeInputs.tsx
+++ b/autogpt_platform/frontend/src/app/(platform)/build/components/legacy-builder/NodeInputs.tsx
@@ -3,7 +3,7 @@ import {
CustomNodeData,
} from "@/app/(platform)/build/components/legacy-builder/CustomNode/CustomNode";
import { NodeTableInput } from "@/app/(platform)/build/components/legacy-builder/NodeTableInput";
-import { CredentialsInput } from "@/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/modals/CredentialsInputs/CredentialsInputs";
+import { CredentialsInput } from "@/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/modals/CredentialsInputs/CredentialsInput";
import { Button } from "@/components/__legacy__/ui/button";
import { Calendar } from "@/components/__legacy__/ui/calendar";
import { LocalValuedInput } from "@/components/__legacy__/ui/input";
diff --git a/autogpt_platform/frontend/src/app/(platform)/chat/components/ChatCredentialsSetup/ChatCredentialsSetup.tsx b/autogpt_platform/frontend/src/app/(platform)/chat/components/ChatCredentialsSetup/ChatCredentialsSetup.tsx
index 1f70f7740f..3868e17a10 100644
--- a/autogpt_platform/frontend/src/app/(platform)/chat/components/ChatCredentialsSetup/ChatCredentialsSetup.tsx
+++ b/autogpt_platform/frontend/src/app/(platform)/chat/components/ChatCredentialsSetup/ChatCredentialsSetup.tsx
@@ -1,4 +1,4 @@
-import { CredentialsInput } from "@/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/modals/CredentialsInputs/CredentialsInputs";
+import { CredentialsInput } from "@/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/modals/CredentialsInputs/CredentialsInput";
import { Card } from "@/components/atoms/Card/Card";
import { Text } from "@/components/atoms/Text/Text";
import type { BlockIOCredentialsSubSchema } from "@/lib/autogpt-server-api";
diff --git a/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/NewAgentLibraryView.tsx b/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/NewAgentLibraryView.tsx
index 3768a0d150..e184bc59f1 100644
--- a/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/NewAgentLibraryView.tsx
+++ b/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/NewAgentLibraryView.tsx
@@ -1,32 +1,31 @@
"use client";
import { Button } from "@/components/atoms/Button/Button";
+import { PublishAgentModal } from "@/components/contextual/PublishAgentModal/PublishAgentModal";
import { Breadcrumbs } from "@/components/molecules/Breadcrumbs/Breadcrumbs";
import { ErrorCard } from "@/components/molecules/ErrorCard/ErrorCard";
import { cn } from "@/lib/utils";
import { PlusIcon } from "@phosphor-icons/react";
import { useEffect, useState } from "react";
-import { RunAgentModal } from "./components/modals/RunAgentModal/RunAgentModal";
-import { useMarketplaceUpdate } from "./hooks/useMarketplaceUpdate";
import { AgentVersionChangelog } from "./components/AgentVersionChangelog";
-import { MarketplaceBanners } from "@/components/contextual/MarketplaceBanners/MarketplaceBanners";
-import { PublishAgentModal } from "@/components/contextual/PublishAgentModal/PublishAgentModal";
-import { AgentSettingsButton } from "./components/other/AgentSettingsButton";
+import { AgentSettingsModal } from "./components/modals/AgentSettingsModal/AgentSettingsModal";
+import { RunAgentModal } from "./components/modals/RunAgentModal/RunAgentModal";
import { AgentRunsLoading } from "./components/other/AgentRunsLoading";
import { EmptySchedules } from "./components/other/EmptySchedules";
import { EmptyTasks } from "./components/other/EmptyTasks";
import { EmptyTemplates } from "./components/other/EmptyTemplates";
import { EmptyTriggers } from "./components/other/EmptyTriggers";
+import { MarketplaceBanners } from "./components/other/MarketplaceBanners";
import { SectionWrap } from "./components/other/SectionWrap";
import { LoadingSelectedContent } from "./components/selected-views/LoadingSelectedContent";
import { SelectedRunView } from "./components/selected-views/SelectedRunView/SelectedRunView";
import { SelectedScheduleView } from "./components/selected-views/SelectedScheduleView/SelectedScheduleView";
-import { SelectedSettingsView } from "./components/selected-views/SelectedSettingsView/SelectedSettingsView";
import { SelectedTemplateView } from "./components/selected-views/SelectedTemplateView/SelectedTemplateView";
import { SelectedTriggerView } from "./components/selected-views/SelectedTriggerView/SelectedTriggerView";
import { SelectedViewLayout } from "./components/selected-views/SelectedViewLayout";
import { SidebarRunsList } from "./components/sidebar/SidebarRunsList/SidebarRunsList";
import { AGENT_LIBRARY_SECTION_PADDING_X } from "./helpers";
+import { useMarketplaceUpdate } from "./hooks/useMarketplaceUpdate";
import { useNewAgentLibraryView } from "./useNewAgentLibraryView";
export function NewAgentLibraryView() {
@@ -45,7 +44,6 @@ export function NewAgentLibraryView() {
handleSelectRun,
handleCountsChange,
handleClearSelectedRun,
- handleSelectSettings,
onRunInitiated,
onTriggerSetup,
onScheduleCreated,
@@ -137,13 +135,16 @@ export function NewAgentLibraryView() {
return (
<>
-
-
+
-
-
- New task
-
- }
- agent={agent}
- onRunCreated={onRunInitiated}
- onScheduleCreated={onScheduleCreated}
- onTriggerSetup={onTriggerSetup}
- initialInputValues={activeTemplate?.inputs}
- initialInputCredentials={activeTemplate?.credentials}
- />
-
-
+
+ New task
+
+ }
+ agent={agent}
+ onRunCreated={onRunInitiated}
+ onScheduleCreated={onScheduleCreated}
+ onTriggerSetup={onTriggerSetup}
+ initialInputValues={activeTemplate?.inputs}
+ initialInputCredentials={activeTemplate?.credentials}
+ />
{activeItem ? (
- activeItem === "settings" ? (
-
- ) : activeTab === "scheduled" ? (
+ activeTab === "scheduled" ? (
)
) : sidebarLoading ? (
diff --git a/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/modals/AgentInputsReadOnly/AgentInputsReadOnly.tsx b/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/modals/AgentInputsReadOnly/AgentInputsReadOnly.tsx
index bc9918c2bb..a66c6ecfc0 100644
--- a/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/modals/AgentInputsReadOnly/AgentInputsReadOnly.tsx
+++ b/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/modals/AgentInputsReadOnly/AgentInputsReadOnly.tsx
@@ -3,7 +3,8 @@
import type { LibraryAgent } from "@/app/api/__generated__/models/libraryAgent";
import { Text } from "@/components/atoms/Text/Text";
import type { CredentialsMetaInput } from "@/lib/autogpt-server-api/types";
-import { CredentialsInput } from "../CredentialsInputs/CredentialsInputs";
+import { CredentialsInput } from "../CredentialsInputs/CredentialsInput";
+import { isSystemCredential } from "../CredentialsInputs/helpers";
import { RunAgentInputs } from "../RunAgentInputs/RunAgentInputs";
import { getAgentCredentialsFields, getAgentInputFields } from "./helpers";
@@ -71,6 +72,7 @@ export function AgentInputsReadOnly({
{credentialFieldEntries.map(([key, inputSubSchema]) => {
const credential = credentialInputs![key];
if (!credential) return null;
+ if (isSystemCredential(credential)) return null;
return (
void;
+}
+
+export function AgentSettingsModal({
+ agent,
+ controlledOpen,
+ onOpenChange,
+}: Props) {
+ const [internalIsOpen, setInternalIsOpen] = useState(false);
+ const isOpen = controlledOpen !== undefined ? controlledOpen : internalIsOpen;
+
+ function setIsOpen(open: boolean) {
+ if (onOpenChange) {
+ onOpenChange(open);
+ } else {
+ setInternalIsOpen(open);
+ }
+ }
+
+ const { currentSafeMode, isPending, hasHITLBlocks, handleToggle } =
+ useAgentSafeMode(agent);
+
+ if (!hasHITLBlocks) return null;
+
+ return (
+
+ );
+}
diff --git a/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/modals/CredentialsInputs/CredentialsInputs.tsx b/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/modals/CredentialsInputs/CredentialsInput.tsx
similarity index 52%
rename from autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/modals/CredentialsInputs/CredentialsInputs.tsx
rename to autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/modals/CredentialsInputs/CredentialsInput.tsx
index a0f9376aa2..7fc88c1488 100644
--- a/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/modals/CredentialsInputs/CredentialsInputs.tsx
+++ b/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/modals/CredentialsInputs/CredentialsInput.tsx
@@ -1,6 +1,4 @@
-import { Button } from "@/components/atoms/Button/Button";
import { Text } from "@/components/atoms/Text/Text";
-import { InformationTooltip } from "@/components/molecules/InformationTooltip/InformationTooltip";
import {
BlockIOCredentialsSubSchema,
CredentialsMetaInput,
@@ -8,13 +6,11 @@ import {
import { cn } from "@/lib/utils";
import { toDisplayName } from "@/providers/agent-credentials/helper";
import { APIKeyCredentialsModal } from "./components/APIKeyCredentialsModal/APIKeyCredentialsModal";
-import { CredentialRow } from "./components/CredentialRow/CredentialRow";
-import { CredentialsSelect } from "./components/CredentialsSelect/CredentialsSelect";
-import { DeleteConfirmationModal } from "./components/DeleteConfirmationModal/DeleteConfirmationModal";
+import { CredentialsFlatView } from "./components/CredentialsFlatView/CredentialsFlatView";
import { HostScopedCredentialsModal } from "./components/HotScopedCredentialsModal/HotScopedCredentialsModal";
import { OAuthFlowWaitingModal } from "./components/OAuthWaitingModal/OAuthWaitingModal";
import { PasswordCredentialsModal } from "./components/PasswordCredentialsModal/PasswordCredentialsModal";
-import { getCredentialDisplayName } from "./helpers";
+import { isSystemCredential } from "./helpers";
import {
CredentialsInputState,
useCredentialsInput,
@@ -72,115 +68,53 @@ export function CredentialsInput({
supportsOAuth2,
supportsUserPassword,
supportsHostScoped,
- credentialsToShow,
+ userCredentials,
+ systemCredentials,
oAuthError,
isAPICredentialsModalOpen,
isUserPasswordCredentialsModalOpen,
isHostScopedCredentialsModalOpen,
isOAuth2FlowInProgress,
oAuthPopupController,
- credentialToDelete,
- deleteCredentialsMutation,
actionButtonText,
setAPICredentialsModalOpen,
setUserPasswordCredentialsModalOpen,
setHostScopedCredentialsModalOpen,
- setCredentialToDelete,
handleActionButtonClick,
handleCredentialSelect,
- handleDeleteCredential,
- handleDeleteConfirm,
} = hookData;
const displayName = toDisplayName(provider);
- const hasCredentialsToShow = credentialsToShow.length > 0;
+ const selectedCredentialIsSystem =
+ selectedCredential && isSystemCredential(selectedCredential);
+
+ const allCredentials = [...userCredentials, ...systemCredentials];
+
+ if (readOnly && selectedCredentialIsSystem) {
+ return null;
+ }
return (
- {showTitle && (
-
-
- {displayName} credentials
- {isOptional && (
-
- (optional)
-
- )}
-
- {schema.description && (
-
- )}
-
- )}
-
- {hasCredentialsToShow ? (
- <>
- {(credentialsToShow.length > 1 || isOptional) && !readOnly ? (
-
onSelectCredential(undefined)}
- readOnly={readOnly}
- allowNone={isOptional}
- variant={variant}
- />
- ) : (
-
- {credentialsToShow.map((credential) => {
- return (
- handleCredentialSelect(credential.id)}
- onDelete={() =>
- handleDeleteCredential({
- id: credential.id,
- title: getCredentialDisplayName(
- credential,
- displayName,
- ),
- })
- }
- readOnly={readOnly}
- />
- );
- })}
-
- )}
- {!readOnly && (
-
- )}
- >
- ) : (
- !readOnly && (
-
- )
- )}
+ onSelectCredential(undefined)}
+ onAddCredential={handleActionButtonClick}
+ actionButtonText={actionButtonText}
+ isOptional={isOptional}
+ showTitle={showTitle}
+ readOnly={readOnly}
+ variant={variant}
+ />
{!readOnly && (
<>
- {supportsApiKey ? (
+ {supportsApiKey && (
- ) : null}
- {supportsOAuth2 ? (
+ )}
+ {supportsOAuth2 && (
oAuthPopupController?.abort("canceled")}
providerName={providerName}
/>
- ) : null}
- {supportsUserPassword ? (
+ )}
+ {supportsUserPassword && (
- ) : null}
- {supportsHostScoped ? (
+ )}
+ {supportsHostScoped && (
- ) : null}
+ )}
- {oAuthError ? (
+ {oAuthError && (
Error: {oAuthError}
- ) : null}
-
- setCredentialToDelete(null)}
- onConfirm={handleDeleteConfirm}
- />
+ )}
>
)}
diff --git a/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/modals/CredentialsInputs/components/APIKeyCredentialsModal/APIKeyCredentialsModal.tsx b/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/modals/CredentialsInputs/components/APIKeyCredentialsModal/APIKeyCredentialsModal.tsx
index 0180c4ebf9..90f6c0ff70 100644
--- a/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/modals/CredentialsInputs/components/APIKeyCredentialsModal/APIKeyCredentialsModal.tsx
+++ b/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/modals/CredentialsInputs/components/APIKeyCredentialsModal/APIKeyCredentialsModal.tsx
@@ -1,11 +1,11 @@
-import { Input } from "@/components/atoms/Input/Input";
-import { Button } from "@/components/atoms/Button/Button";
-import { Dialog } from "@/components/molecules/Dialog/Dialog";
import {
Form,
FormDescription,
FormField,
} from "@/components/__legacy__/ui/form";
+import { Button } from "@/components/atoms/Button/Button";
+import { Input } from "@/components/atoms/Input/Input";
+import { Dialog } from "@/components/molecules/Dialog/Dialog";
import {
BlockIOCredentialsSubSchema,
CredentialsMetaInput,
@@ -60,7 +60,23 @@ export function APIKeyCredentialsModal({
)}
diff --git a/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/modals/CredentialsInputs/components/APIKeyCredentialsModal/useAPIKeyCredentialsModal.ts b/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/modals/CredentialsInputs/components/APIKeyCredentialsModal/useAPIKeyCredentialsModal.ts
index 391633bed5..72599a2e79 100644
--- a/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/modals/CredentialsInputs/components/APIKeyCredentialsModal/useAPIKeyCredentialsModal.ts
+++ b/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/modals/CredentialsInputs/components/APIKeyCredentialsModal/useAPIKeyCredentialsModal.ts
@@ -1,11 +1,11 @@
-import { z } from "zod";
-import { useForm, type UseFormReturn } from "react-hook-form";
-import { zodResolver } from "@hookform/resolvers/zod";
import useCredentials from "@/hooks/useCredentials";
import {
BlockIOCredentialsSubSchema,
CredentialsMetaInput,
} from "@/lib/autogpt-server-api/types";
+import { zodResolver } from "@hookform/resolvers/zod";
+import { useForm, type UseFormReturn } from "react-hook-form";
+import { z } from "zod";
export type APIKeyFormValues = {
apiKey: string;
@@ -40,12 +40,24 @@ export function useAPIKeyCredentialsModal({
expiresAt: z.string().optional(),
});
+ function getDefaultExpirationDate(): string {
+ const tomorrow = new Date();
+ tomorrow.setDate(tomorrow.getDate() + 1);
+ tomorrow.setHours(0, 0, 0, 0);
+ const year = tomorrow.getFullYear();
+ const month = String(tomorrow.getMonth() + 1).padStart(2, "0");
+ const day = String(tomorrow.getDate()).padStart(2, "0");
+ const hours = String(tomorrow.getHours()).padStart(2, "0");
+ const minutes = String(tomorrow.getMinutes()).padStart(2, "0");
+ return `${year}-${month}-${day}T${hours}:${minutes}`;
+ }
+
const form = useForm({
resolver: zodResolver(formSchema),
defaultValues: {
apiKey: "",
title: "",
- expiresAt: "",
+ expiresAt: getDefaultExpirationDate(),
},
});
diff --git a/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/modals/CredentialsInputs/components/CredentialRow/CredentialRow.tsx b/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/modals/CredentialsInputs/components/CredentialRow/CredentialRow.tsx
index 2d0358aacb..dc69c34d93 100644
--- a/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/modals/CredentialsInputs/components/CredentialRow/CredentialRow.tsx
+++ b/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/modals/CredentialsInputs/components/CredentialRow/CredentialRow.tsx
@@ -7,7 +7,8 @@ import {
DropdownMenuTrigger,
} from "@/components/molecules/DropdownMenu/DropdownMenu";
import { cn } from "@/lib/utils";
-import { CaretDown, DotsThreeVertical } from "@phosphor-icons/react";
+import { CaretDownIcon, DotsThreeVertical } from "@phosphor-icons/react";
+import { useEffect, useRef, useState } from "react";
import {
fallbackIcon,
getCredentialDisplayName,
@@ -26,7 +27,7 @@ type CredentialRowProps = {
provider: string;
displayName: string;
onSelect: () => void;
- onDelete: () => void;
+ onDelete?: () => void;
readOnly?: boolean;
showCaret?: boolean;
asSelectTrigger?: boolean;
@@ -47,11 +48,32 @@ export function CredentialRow({
}: CredentialRowProps) {
const ProviderIcon = providerIcons[provider] || fallbackIcon;
const isNodeVariant = variant === "node";
+ const containerRef = useRef(null);
+ const [showMaskedKey, setShowMaskedKey] = useState(true);
+
+ useEffect(() => {
+ const container = containerRef.current;
+ if (!container) return;
+
+ const resizeObserver = new ResizeObserver((entries) => {
+ for (const entry of entries) {
+ const width = entry.contentRect.width;
+ setShowMaskedKey(width >= 360);
+ }
+ });
+
+ resizeObserver.observe(container);
+
+ return () => {
+ resizeObserver.disconnect();
+ };
+ }, []);
return (
{getCredentialDisplayName(credential, displayName)}
- {!(asSelectTrigger && isNodeVariant) && (
+ {!(asSelectTrigger && isNodeVariant) && showMaskedKey && (
{"*".repeat(MASKED_KEY_LENGTH)}
)}
- {showCaret && !asSelectTrigger && (
-
+ {(showCaret || (asSelectTrigger && !readOnly)) && (
+
)}
- {!readOnly && !showCaret && !asSelectTrigger && (
+ {!readOnly && !showCaret && !asSelectTrigger && onDelete && (
);
}
diff --git a/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/modals/CredentialsInputs/helpers.ts b/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/modals/CredentialsInputs/helpers.ts
index 4cca825747..ef965d5382 100644
--- a/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/modals/CredentialsInputs/helpers.ts
+++ b/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/modals/CredentialsInputs/helpers.ts
@@ -99,4 +99,30 @@ export function getCredentialDisplayName(
}
export const OAUTH_TIMEOUT_MS = 5 * 60 * 1000;
-export const MASKED_KEY_LENGTH = 30;
+export const MASKED_KEY_LENGTH = 15;
+
+export function isSystemCredential(credential: {
+ title?: string | null;
+ is_system?: boolean;
+}): boolean {
+ if (credential.is_system === true) return true;
+ if (!credential.title) return false;
+ const titleLower = credential.title.toLowerCase();
+ return (
+ titleLower.includes("system") ||
+ titleLower.startsWith("use credits for") ||
+ titleLower.includes("use credits")
+ );
+}
+
+export function filterSystemCredentials<
+ T extends { title?: string; is_system?: boolean },
+>(credentials: T[]): T[] {
+ return credentials.filter((cred) => !isSystemCredential(cred));
+}
+
+export function getSystemCredentials<
+ T extends { title?: string; is_system?: boolean },
+>(credentials: T[]): T[] {
+ return credentials.filter((cred) => isSystemCredential(cred));
+}
diff --git a/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/modals/CredentialsInputs/useCredentialsInput.ts b/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/modals/CredentialsInputs/useCredentialsInput.ts
index c780ffeffc..8876ddcba9 100644
--- a/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/modals/CredentialsInputs/useCredentialsInput.ts
+++ b/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/modals/CredentialsInputs/useCredentialsInput.ts
@@ -6,9 +6,11 @@ import {
CredentialsMetaInput,
} from "@/lib/autogpt-server-api/types";
import { useQueryClient } from "@tanstack/react-query";
-import { useEffect, useMemo, useState } from "react";
+import { useEffect, useRef, useState } from "react";
import {
+ filterSystemCredentials,
getActionButtonText,
+ getSystemCredentials,
OAUTH_TIMEOUT_MS,
OAuthPopupResultMessage,
} from "./helpers";
@@ -54,6 +56,7 @@ export function useCredentialsInput({
const api = useBackendAPI();
const queryClient = useQueryClient();
const credentials = useCredentials(schema, siblingInputs);
+ const hasAttemptedAutoSelect = useRef(false);
const deleteCredentialsMutation = useDeleteV1DeleteCredentials({
mutation: {
@@ -82,38 +85,51 @@ export function useCredentialsInput({
useEffect(() => {
if (readOnly) return;
if (!credentials || !("savedCredentials" in credentials)) return;
+ const availableCreds = credentials.savedCredentials;
if (
selectedCredential &&
- !credentials.savedCredentials.some((c) => c.id === selectedCredential.id)
+ !availableCreds.some((c) => c.id === selectedCredential.id)
) {
onSelectCredential(undefined);
+ // Reset auto-selection flag so it can run again after unsetting invalid credential
+ hasAttemptedAutoSelect.current = false;
}
}, [credentials, selectedCredential, onSelectCredential, readOnly]);
- // The available credential, if there is only one
- const singleCredential = useMemo(() => {
- if (!credentials || !("savedCredentials" in credentials)) {
- return null;
- }
-
- return credentials.savedCredentials.length === 1
- ? credentials.savedCredentials[0]
- : null;
- }, [credentials]);
-
- // Auto-select the one available credential (only if not optional)
+ // Auto-select the first available credential on initial mount
+ // Once a user has made a selection, we don't override it
useEffect(() => {
if (readOnly) return;
- if (isOptional) return; // Don't auto-select when credential is optional
- if (singleCredential && !selectedCredential) {
- onSelectCredential(singleCredential);
+ if (!credentials || !("savedCredentials" in credentials)) return;
+
+ // If already selected, don't auto-select
+ if (selectedCredential?.id) return;
+
+ // Only attempt auto-selection once
+ if (hasAttemptedAutoSelect.current) return;
+ hasAttemptedAutoSelect.current = true;
+
+ // If optional, don't auto-select (user can choose "None")
+ if (isOptional) return;
+
+ const savedCreds = credentials.savedCredentials;
+
+ // Auto-select the first credential if any are available
+ if (savedCreds.length > 0) {
+ const cred = savedCreds[0];
+ onSelectCredential({
+ id: cred.id,
+ type: cred.type,
+ provider: credentials.provider,
+ title: (cred as any).title,
+ });
}
}, [
- singleCredential,
- selectedCredential,
- onSelectCredential,
+ credentials,
+ selectedCredential?.id,
readOnly,
isOptional,
+ onSelectCredential,
]);
if (
@@ -135,8 +151,13 @@ export function useCredentialsInput({
supportsHostScoped,
savedCredentials,
oAuthCallback,
+ isSystemProvider,
} = credentials;
+ // Split credentials into user and system
+ const userCredentials = filterSystemCredentials(savedCredentials);
+ const systemCredentials = getSystemCredentials(savedCredentials);
+
async function handleOAuthLogin() {
setOAuthError(null);
const { login_url, state_token } = await api.oAuthLogin(
@@ -291,7 +312,10 @@ export function useCredentialsInput({
supportsOAuth2,
supportsUserPassword,
supportsHostScoped,
- credentialsToShow: savedCredentials,
+ isSystemProvider,
+ userCredentials,
+ systemCredentials,
+ allCredentials: savedCredentials,
selectedCredential,
oAuthError,
isAPICredentialsModalOpen,
@@ -306,7 +330,7 @@ export function useCredentialsInput({
supportsApiKey,
supportsUserPassword,
supportsHostScoped,
- savedCredentials.length > 0,
+ userCredentials.length > 0,
),
setAPICredentialsModalOpen,
setUserPasswordCredentialsModalOpen,
diff --git a/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/modals/RunAgentModal/RunAgentModal.tsx b/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/modals/RunAgentModal/RunAgentModal.tsx
index e53f31a349..cd0c666be6 100644
--- a/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/modals/RunAgentModal/RunAgentModal.tsx
+++ b/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/modals/RunAgentModal/RunAgentModal.tsx
@@ -12,7 +12,7 @@ import {
TooltipTrigger,
} from "@/components/atoms/Tooltip/BaseTooltip";
import { Dialog } from "@/components/molecules/Dialog/Dialog";
-import { useState } from "react";
+import { useEffect, useRef, useState } from "react";
import { ScheduleAgentModal } from "../ScheduleAgentModal/ScheduleAgentModal";
import { ModalHeader } from "./components/ModalHeader/ModalHeader";
import { ModalRunSection } from "./components/ModalRunSection/ModalRunSection";
@@ -82,6 +82,8 @@ export function RunAgentModal({
});
const [isScheduleModalOpen, setIsScheduleModalOpen] = useState(false);
+ const [hasOverflow, setHasOverflow] = useState(false);
+ const contentRef = useRef
(null);
const hasAnySetupFields =
Object.keys(agentInputFields || {}).length > 0 ||
@@ -89,6 +91,43 @@ export function RunAgentModal({
const isTriggerRunType = defaultRunType.includes("trigger");
+ useEffect(() => {
+ if (!isOpen) return;
+
+ function checkOverflow() {
+ if (!contentRef.current) return;
+ const scrollableParent = contentRef.current
+ .closest("[data-dialog-content]")
+ ?.querySelector('[class*="overflow-y-auto"]');
+ if (scrollableParent) {
+ setHasOverflow(
+ scrollableParent.scrollHeight > scrollableParent.clientHeight,
+ );
+ }
+ }
+
+ const timeoutId = setTimeout(checkOverflow, 100);
+ const resizeObserver = new ResizeObserver(checkOverflow);
+ if (contentRef.current) {
+ const scrollableParent = contentRef.current
+ .closest("[data-dialog-content]")
+ ?.querySelector('[class*="overflow-y-auto"]');
+ if (scrollableParent) {
+ resizeObserver.observe(scrollableParent);
+ }
+ }
+
+ return () => {
+ clearTimeout(timeoutId);
+ resizeObserver.disconnect();
+ };
+ }, [
+ isOpen,
+ hasAnySetupFields,
+ agentInputFields,
+ agentCredentialsInputFields,
+ ]);
+
function handleInputChange(key: string, value: string) {
setInputValues((prev) => ({
...prev,
@@ -134,91 +173,97 @@ export function RunAgentModal({
>
{triggerSlot}
- {/* Header */}
-
+
+
+ {/* Header */}
+
- {/* Content */}
- {hasAnySetupFields ? (
-
-
-
-
+ {/* Content */}
+ {hasAnySetupFields ? (
+
+
+
+
+
+ ) : null}
- ) : null}
-
-
- {isTriggerRunType ? null : !allRequiredInputsAreSet ? (
-
-
-
-
-
- Schedule Task
-
-
-
-
-
- Please set up all required inputs and credentials before
- scheduling
-
-
-
-
- ) : (
-
- Schedule Task
-
- )}
-
+
+ {isTriggerRunType ? null : !allRequiredInputsAreSet ? (
+
+
+
+
+
+ Schedule Task
+
+
+
+
+
+ Please set up all required inputs and credentials
+ before scheduling
+
+
+
+
+ ) : (
+
+ Schedule Task
+
+ )}
+
+
+
-
-
-
+
+
>
diff --git a/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/modals/RunAgentModal/components/CredentialsGroupedView/CredentialsGroupedView.tsx b/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/modals/RunAgentModal/components/CredentialsGroupedView/CredentialsGroupedView.tsx
new file mode 100644
index 0000000000..05b2966af7
--- /dev/null
+++ b/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/modals/RunAgentModal/components/CredentialsGroupedView/CredentialsGroupedView.tsx
@@ -0,0 +1,181 @@
+import { CredentialsInput } from "@/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/modals/CredentialsInputs/CredentialsInput";
+import {
+ Accordion,
+ AccordionContent,
+ AccordionItem,
+ AccordionTrigger,
+} from "@/components/molecules/Accordion/Accordion";
+import { CredentialsProvidersContext } from "@/providers/agent-credentials/credentials-provider";
+import { SlidersHorizontal } from "@phosphor-icons/react";
+import { useContext, useEffect, useMemo, useRef } from "react";
+import { useRunAgentModalContext } from "../../context";
+import {
+ areSystemCredentialProvidersLoading,
+ CredentialField,
+ findSavedCredentialByProviderAndType,
+ hasMissingRequiredSystemCredentials,
+ splitCredentialFieldsBySystem,
+} from "../helpers";
+
+type Props = {
+ credentialFields: CredentialField[];
+ requiredCredentials: Set
;
+};
+
+export function CredentialsGroupedView({
+ credentialFields,
+ requiredCredentials,
+}: Props) {
+ const allProviders = useContext(CredentialsProvidersContext);
+ const { inputCredentials, setInputCredentialsValue, inputValues } =
+ useRunAgentModalContext();
+
+ const { userCredentialFields, systemCredentialFields } = useMemo(
+ () =>
+ splitCredentialFieldsBySystem(
+ credentialFields,
+ allProviders,
+ inputCredentials,
+ ),
+ [credentialFields, allProviders, inputCredentials],
+ );
+
+ const hasSystemCredentials = systemCredentialFields.length > 0;
+ const hasUserCredentials = userCredentialFields.length > 0;
+ const hasAttemptedAutoSelect = useRef(false);
+
+ const isLoadingProviders = useMemo(
+ () =>
+ areSystemCredentialProvidersLoading(systemCredentialFields, allProviders),
+ [systemCredentialFields, allProviders],
+ );
+
+ const hasMissingSystemCredentials = useMemo(() => {
+ if (isLoadingProviders) return false;
+ return hasMissingRequiredSystemCredentials(
+ systemCredentialFields,
+ requiredCredentials,
+ inputCredentials,
+ allProviders,
+ );
+ }, [
+ isLoadingProviders,
+ systemCredentialFields,
+ requiredCredentials,
+ inputCredentials,
+ allProviders,
+ ]);
+
+ useEffect(() => {
+ if (hasAttemptedAutoSelect.current) return;
+ if (!hasSystemCredentials) return;
+ if (isLoadingProviders) return;
+
+ for (const [key, schema] of systemCredentialFields) {
+ const alreadySelected = inputCredentials?.[key];
+ const isRequired = requiredCredentials.has(key);
+ if (alreadySelected || !isRequired) continue;
+
+ const providerNames = schema.credentials_provider || [];
+ const credentialTypes = schema.credentials_types || [];
+ const requiredScopes = schema.credentials_scopes;
+ const savedCredential = findSavedCredentialByProviderAndType(
+ providerNames,
+ credentialTypes,
+ requiredScopes,
+ allProviders,
+ );
+
+ if (savedCredential) {
+ setInputCredentialsValue(key, {
+ id: savedCredential.id,
+ provider: savedCredential.provider,
+ type: savedCredential.type,
+ title: (savedCredential as { title?: string }).title,
+ });
+ }
+ }
+
+ hasAttemptedAutoSelect.current = true;
+ }, [
+ allProviders,
+ hasSystemCredentials,
+ systemCredentialFields,
+ requiredCredentials,
+ inputCredentials,
+ setInputCredentialsValue,
+ isLoadingProviders,
+ ]);
+
+ return (
+
+ {hasUserCredentials && (
+ <>
+ {userCredentialFields.map(
+ ([key, inputSubSchema]: CredentialField) => {
+ const selectedCred = inputCredentials?.[key];
+
+ return (
+
{
+ setInputCredentialsValue(key, value);
+ }}
+ siblingInputs={inputValues}
+ isOptional={!requiredCredentials.has(key)}
+ />
+ );
+ },
+ )}
+ >
+ )}
+
+ {hasSystemCredentials && (
+
+
+
+
+ System credentials
+ {hasMissingSystemCredentials && (
+ (missing)
+ )}
+
+
+
+
+ {systemCredentialFields.map(
+ ([key, inputSubSchema]: CredentialField) => {
+ const selectedCred = inputCredentials?.[key];
+
+ return (
+ {
+ setInputCredentialsValue(key, value);
+ }}
+ siblingInputs={inputValues}
+ isOptional={!requiredCredentials.has(key)}
+ />
+ );
+ },
+ )}
+
+
+
+
+ )}
+
+ );
+}
diff --git a/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/modals/RunAgentModal/components/ModalRunSection/ModalRunSection.tsx b/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/modals/RunAgentModal/components/ModalRunSection/ModalRunSection.tsx
index aba4caee7a..7660de7c15 100644
--- a/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/modals/RunAgentModal/components/ModalRunSection/ModalRunSection.tsx
+++ b/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/modals/RunAgentModal/components/ModalRunSection/ModalRunSection.tsx
@@ -1,8 +1,9 @@
-import { CredentialsInput } from "@/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/modals/CredentialsInputs/CredentialsInputs";
import { Input } from "@/components/atoms/Input/Input";
import { InformationTooltip } from "@/components/molecules/InformationTooltip/InformationTooltip";
+import { useMemo } from "react";
import { RunAgentInputs } from "../../../RunAgentInputs/RunAgentInputs";
import { useRunAgentModalContext } from "../../context";
+import { CredentialsGroupedView } from "../CredentialsGroupedView/CredentialsGroupedView";
import { ModalSection } from "../ModalSection/ModalSection";
import { WebhookTriggerBanner } from "../WebhookTriggerBanner/WebhookTriggerBanner";
@@ -17,15 +18,16 @@ export function ModalRunSection() {
inputValues,
setInputValue,
agentInputFields,
- inputCredentials,
- setInputCredentialsValue,
agentCredentialsInputFields,
} = useRunAgentModalContext();
const inputFields = Object.entries(agentInputFields || {});
- const credentialFields = Object.entries(agentCredentialsInputFields || {});
- // Get the list of required credentials from the schema
+ const credentialFields = useMemo(() => {
+ if (!agentCredentialsInputFields) return [];
+ return Object.entries(agentCredentialsInputFields);
+ }, [agentCredentialsInputFields]);
+
const requiredCredentials = new Set(
(agent.credentials_input_schema?.required as string[]) || [],
);
@@ -97,24 +99,10 @@ export function ModalRunSection() {
title="Task Credentials"
subtitle="These are the credentials the agent will use to perform this task"
>
-
- {Object.entries(agentCredentialsInputFields || {}).map(
- ([key, inputSubSchema]) => (
-
- setInputCredentialsValue(key, value)
- }
- siblingInputs={inputValues}
- isOptional={!requiredCredentials.has(key)}
- />
- ),
- )}
-
+
) : null}
diff --git a/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/modals/RunAgentModal/components/helpers.ts b/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/modals/RunAgentModal/components/helpers.ts
new file mode 100644
index 0000000000..61267f733d
--- /dev/null
+++ b/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/modals/RunAgentModal/components/helpers.ts
@@ -0,0 +1,210 @@
+import { CredentialsProvidersContextType } from "@/providers/agent-credentials/credentials-provider";
+import { getSystemCredentials } from "../../CredentialsInputs/helpers";
+
+export type CredentialField = [string, any];
+
+type SavedCredential = {
+ id: string;
+ provider: string;
+ type: string;
+ title?: string | null;
+};
+
+function hasRequiredScopes(
+ credential: { scopes?: string[]; type: string },
+ requiredScopes?: string[],
+) {
+ if (credential.type !== "oauth2") return true;
+ if (!requiredScopes || requiredScopes.length === 0) return true;
+ const grantedScopes = new Set(credential.scopes || []);
+ for (const scope of requiredScopes) {
+ if (!grantedScopes.has(scope)) return false;
+ }
+ return true;
+}
+
+export function splitCredentialFieldsBySystem(
+ credentialFields: CredentialField[],
+ allProviders: CredentialsProvidersContextType | null,
+ inputCredentials?: Record,
+) {
+ if (!allProviders || credentialFields.length === 0) {
+ return {
+ userCredentialFields: [] as CredentialField[],
+ systemCredentialFields: [] as CredentialField[],
+ };
+ }
+
+ const userFields: CredentialField[] = [];
+ const systemFields: CredentialField[] = [];
+
+ for (const [key, schema] of credentialFields) {
+ const providerNames = schema.credentials_provider || [];
+ const isSystemField = providerNames.some((providerName: string) => {
+ const providerData = allProviders[providerName];
+ return providerData?.isSystemProvider === true;
+ });
+
+ if (isSystemField) {
+ systemFields.push([key, schema]);
+ } else {
+ userFields.push([key, schema]);
+ }
+ }
+
+ const sortByUnsetFirst = (a: CredentialField, b: CredentialField) => {
+ const aIsSet = Boolean(inputCredentials?.[a[0]]);
+ const bIsSet = Boolean(inputCredentials?.[b[0]]);
+
+ if (aIsSet === bIsSet) return 0;
+ return aIsSet ? 1 : -1;
+ };
+
+ return {
+ userCredentialFields: userFields.sort(sortByUnsetFirst),
+ systemCredentialFields: systemFields.sort(sortByUnsetFirst),
+ };
+}
+
+export function areSystemCredentialProvidersLoading(
+ systemCredentialFields: CredentialField[],
+ allProviders: CredentialsProvidersContextType | null,
+): boolean {
+ if (!systemCredentialFields.length) return false;
+ if (allProviders === null) return true;
+
+ for (const [_, schema] of systemCredentialFields) {
+ const providerNames = schema.credentials_provider || [];
+ const hasAllProviders = providerNames.every(
+ (providerName: string) => allProviders?.[providerName] !== undefined,
+ );
+ if (!hasAllProviders) return true;
+ }
+
+ return false;
+}
+
+export function hasMissingRequiredSystemCredentials(
+ systemCredentialFields: CredentialField[],
+ requiredCredentials: Set,
+ inputCredentials?: Record,
+ allProviders?: CredentialsProvidersContextType | null,
+) {
+ if (systemCredentialFields.length === 0) return false;
+ if (allProviders === null) return false;
+
+ return systemCredentialFields.some(([key, schema]) => {
+ if (!requiredCredentials.has(key)) return false;
+ if (inputCredentials?.[key]) return false;
+
+ const providerNames = schema.credentials_provider || [];
+ const credentialTypes = schema.credentials_types || [];
+ const requiredScopes = schema.credentials_scopes;
+
+ return !hasAvailableSystemCredential(
+ providerNames,
+ credentialTypes,
+ requiredScopes,
+ allProviders,
+ );
+ });
+}
+
+function hasAvailableSystemCredential(
+ providerNames: string[],
+ credentialTypes: string[],
+ requiredScopes: string[] | undefined,
+ allProviders: CredentialsProvidersContextType | null | undefined,
+) {
+ if (!allProviders) return false;
+
+ for (const providerName of providerNames) {
+ const providerData = allProviders[providerName];
+ if (!providerData) continue;
+
+ const systemCredentials = getSystemCredentials(
+ providerData.savedCredentials ?? [],
+ );
+
+ for (const credential of systemCredentials) {
+ const typeMatches =
+ credentialTypes.length === 0 ||
+ credentialTypes.includes(credential.type);
+ const scopesMatch = hasRequiredScopes(credential, requiredScopes);
+
+ if (!typeMatches) continue;
+ if (!scopesMatch) continue;
+
+ return true;
+ }
+
+ const allCredentials = providerData.savedCredentials ?? [];
+ for (const credential of allCredentials) {
+ const typeMatches =
+ credentialTypes.length === 0 ||
+ credentialTypes.includes(credential.type);
+ const scopesMatch = hasRequiredScopes(credential, requiredScopes);
+
+ if (!typeMatches) continue;
+ if (!scopesMatch) continue;
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
+export function findSavedCredentialByProviderAndType(
+ providerNames: string[],
+ credentialTypes: string[],
+ requiredScopes: string[] | undefined,
+ allProviders: CredentialsProvidersContextType | null,
+): SavedCredential | undefined {
+ for (const providerName of providerNames) {
+ const providerData = allProviders?.[providerName];
+ if (!providerData) continue;
+
+ const systemCredentials = getSystemCredentials(
+ providerData.savedCredentials ?? [],
+ );
+
+ const matchingCredentials: SavedCredential[] = [];
+
+ for (const credential of systemCredentials) {
+ const typeMatches =
+ credentialTypes.length === 0 ||
+ credentialTypes.includes(credential.type);
+ const scopesMatch = hasRequiredScopes(credential, requiredScopes);
+
+ if (!typeMatches) continue;
+ if (!scopesMatch) continue;
+
+ matchingCredentials.push(credential as SavedCredential);
+ }
+
+ if (matchingCredentials.length === 0) {
+ const allCredentials = providerData.savedCredentials ?? [];
+ for (const credential of allCredentials) {
+ const typeMatches =
+ credentialTypes.length === 0 ||
+ credentialTypes.includes(credential.type);
+ const scopesMatch = hasRequiredScopes(credential, requiredScopes);
+
+ if (!typeMatches) continue;
+ if (!scopesMatch) continue;
+
+ matchingCredentials.push(credential as SavedCredential);
+ }
+ }
+
+ if (matchingCredentials.length === 1) {
+ return matchingCredentials[0];
+ }
+ if (matchingCredentials.length > 1) {
+ return undefined;
+ }
+ }
+
+ return undefined;
+}
diff --git a/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/modals/RunAgentModal/useAgentRunModal.tsx b/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/modals/RunAgentModal/useAgentRunModal.tsx
index eb32083004..3aafd4be50 100644
--- a/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/modals/RunAgentModal/useAgentRunModal.tsx
+++ b/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/modals/RunAgentModal/useAgentRunModal.tsx
@@ -11,9 +11,18 @@ import { LibraryAgent } from "@/app/api/__generated__/models/libraryAgent";
import { LibraryAgentPreset } from "@/app/api/__generated__/models/libraryAgentPreset";
import { useToast } from "@/components/molecules/Toast/use-toast";
import { isEmpty } from "@/lib/utils";
+import { CredentialsProvidersContext } from "@/providers/agent-credentials/credentials-provider";
import { analytics } from "@/services/analytics";
import { useQueryClient } from "@tanstack/react-query";
-import { useCallback, useEffect, useMemo, useState } from "react";
+import {
+ useCallback,
+ useContext,
+ useEffect,
+ useMemo,
+ useRef,
+ useState,
+} from "react";
+import { getSystemCredentials } from "../CredentialsInputs/helpers";
import { showExecutionErrorToast } from "./errorHelpers";
export type RunVariant =
@@ -42,8 +51,10 @@ export function useAgentRunModal(
const [inputCredentials, setInputCredentials] = useState>(
callbacks?.initialInputCredentials || {},
);
+
const [presetName, setPresetName] = useState("");
const [presetDescription, setPresetDescription] = useState("");
+ const hasInitializedSystemCreds = useRef(false);
// Determine the default run type based on agent capabilities
const defaultRunType: RunVariant = agent.trigger_setup_info
@@ -58,6 +69,91 @@ export function useAgentRunModal(
setInputCredentials(callbacks?.initialInputCredentials || {});
}, [callbacks?.initialInputValues, callbacks?.initialInputCredentials]);
+ const allProviders = useContext(CredentialsProvidersContext);
+
+ // Initialize credentials with default system credentials
+ useEffect(() => {
+ if (!allProviders || !agent.credentials_input_schema?.properties) return;
+ if (callbacks?.initialInputCredentials) {
+ hasInitializedSystemCreds.current = true;
+ return;
+ }
+ if (hasInitializedSystemCreds.current) return;
+
+ const properties = agent.credentials_input_schema.properties as Record<
+ string,
+ any
+ >;
+
+ setInputCredentials((currentCreds) => {
+ const credsToAdd: Record = {};
+
+ for (const [key, schema] of Object.entries(properties)) {
+ if (currentCreds[key]) continue;
+
+ const providerNames = schema.credentials_provider || [];
+ const supportedTypes = schema.credentials_types || [];
+ const requiredScopes = schema.credentials_scopes;
+
+ for (const providerName of providerNames) {
+ const providerData = allProviders[providerName];
+ if (!providerData) continue;
+
+ const systemCreds = getSystemCredentials(
+ providerData.savedCredentials ?? [],
+ );
+ const matchingSystemCreds = systemCreds.filter((cred) => {
+ if (!supportedTypes.includes(cred.type)) return false;
+
+ if (
+ cred.type === "oauth2" &&
+ requiredScopes &&
+ requiredScopes.length > 0
+ ) {
+ const grantedScopes = new Set(cred.scopes || []);
+ const hasAllRequiredScopes = requiredScopes.every(
+ (scope: string) => grantedScopes.has(scope),
+ );
+ if (!hasAllRequiredScopes) return false;
+ }
+
+ return true;
+ });
+
+ if (matchingSystemCreds.length === 1) {
+ const systemCred = matchingSystemCreds[0];
+ credsToAdd[key] = {
+ id: systemCred.id,
+ type: systemCred.type,
+ provider: providerName,
+ title: systemCred.title,
+ };
+ break;
+ }
+ }
+ }
+
+ if (Object.keys(credsToAdd).length > 0) {
+ hasInitializedSystemCreds.current = true;
+ return {
+ ...currentCreds,
+ ...credsToAdd,
+ };
+ }
+
+ return currentCreds;
+ });
+ }, [
+ allProviders,
+ agent.credentials_input_schema,
+ callbacks?.initialInputCredentials,
+ ]);
+
+ // Reset initialization flag when modal closes/opens or agent changes
+ useEffect(() => {
+ hasInitializedSystemCreds.current = false;
+ }, [isOpen, agent.graph_id]);
+
// API mutations
const executeGraphMutation = usePostV1ExecuteGraphAgent({
mutation: {
@@ -66,7 +162,6 @@ export function useAgentRunModal(
toast({
title: "Agent execution started",
});
- // Invalidate runs list for this graph
queryClient.invalidateQueries({
queryKey: getGetV1ListGraphExecutionsQueryKey(agent.graph_id),
});
@@ -163,14 +258,10 @@ export function useAgentRunModal(
}, [agentInputSchema.required, inputValues]);
const [allCredentialsAreSet, missingCredentials] = useMemo(() => {
- // Only check required credentials from schema, not all properties
- // Credentials marked as optional in node metadata won't be in the required array
const requiredCredentials = new Set(
(agent.credentials_input_schema?.required as string[]) || [],
);
- // Check if required credentials have valid id (not just key existence)
- // A credential is valid only if it has an id field set
const missing = [...requiredCredentials].filter((key) => {
const cred = inputCredentials[key];
return !cred || !cred.id;
@@ -184,7 +275,6 @@ export function useAgentRunModal(
[agentCredentialsInputFields],
);
- // Final readiness flag combining inputs + credentials when credentials are shown
const allRequiredInputsAreSet = useMemo(
() =>
allRequiredInputsAreSetRaw &&
@@ -223,7 +313,6 @@ export function useAgentRunModal(
defaultRunType === "automatic-trigger" ||
defaultRunType === "manual-trigger"
) {
- // Setup trigger
if (!presetName.trim()) {
toast({
title: "β οΈ Trigger name required",
@@ -244,9 +333,6 @@ export function useAgentRunModal(
},
});
} else {
- // Manual execution
- // Filter out incomplete credentials (optional ones not selected)
- // Only send credentials that have a valid id field
const validCredentials = Object.fromEntries(
Object.entries(inputCredentials).filter(([_, cred]) => cred && cred.id),
);
@@ -280,41 +366,24 @@ export function useAgentRunModal(
}, [agentInputFields]);
return {
- // UI state
isOpen,
setIsOpen,
-
- // Run mode
defaultRunType: defaultRunType as RunVariant,
-
- // Form: regular inputs
inputValues,
setInputValues,
-
- // Form: credentials
inputCredentials,
setInputCredentials,
-
- // Preset/trigger labels
presetName,
presetDescription,
setPresetName,
setPresetDescription,
-
- // Validation/readiness
allRequiredInputsAreSet,
missingInputs,
-
- // Schemas for rendering
agentInputFields,
agentCredentialsInputFields,
hasInputFields,
-
- // Async states
isExecuting: executeGraphMutation.isPending,
isSettingUpTrigger: setupTriggerMutation.isPending,
-
- // Actions
handleRun,
};
}
diff --git a/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/other/AgentSettingsButton.tsx b/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/other/AgentSettingsButton.tsx
index 11dcbd943f..95fdf826a2 100644
--- a/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/other/AgentSettingsButton.tsx
+++ b/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/other/AgentSettingsButton.tsx
@@ -1,37 +1,17 @@
import { Button } from "@/components/atoms/Button/Button";
+import { Text } from "@/components/atoms/Text/Text";
import { GearIcon } from "@phosphor-icons/react";
-import { LibraryAgent } from "@/app/api/__generated__/models/libraryAgent";
-import { useAgentSafeMode } from "@/hooks/useAgentSafeMode";
-
-interface Props {
- agent: LibraryAgent;
- onSelectSettings: () => void;
- selected?: boolean;
-}
-
-export function AgentSettingsButton({
- agent,
- onSelectSettings,
- selected,
-}: Props) {
- const { hasHITLBlocks } = useAgentSafeMode(agent);
-
- if (!hasHITLBlocks) {
- return null;
- }
+export function AgentSettingsButton() {
return (
-
+
+ Agent Settings
);
}
diff --git a/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/other/EmptySchedules.tsx b/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/other/EmptySchedules.tsx
index 97492d8a59..4c781b2896 100644
--- a/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/other/EmptySchedules.tsx
+++ b/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/other/EmptySchedules.tsx
@@ -1,3 +1,5 @@
+"use client";
+
import { Text } from "@/components/atoms/Text/Text";
export function EmptySchedules() {
diff --git a/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/other/EmptyTemplates.tsx b/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/other/EmptyTemplates.tsx
index c33abe69ad..364b762167 100644
--- a/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/other/EmptyTemplates.tsx
+++ b/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/other/EmptyTemplates.tsx
@@ -1,3 +1,5 @@
+"use client";
+
import { Text } from "@/components/atoms/Text/Text";
export function EmptyTemplates() {
diff --git a/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/other/EmptyTriggers.tsx b/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/other/EmptyTriggers.tsx
index 0d9dc47fff..06d09ff9a0 100644
--- a/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/other/EmptyTriggers.tsx
+++ b/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/other/EmptyTriggers.tsx
@@ -1,3 +1,5 @@
+"use client";
+
import { Text } from "@/components/atoms/Text/Text";
export function EmptyTriggers() {
diff --git a/autogpt_platform/frontend/src/components/contextual/MarketplaceBanners/MarketplaceBanners.tsx b/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/other/MarketplaceBanners.tsx
similarity index 97%
rename from autogpt_platform/frontend/src/components/contextual/MarketplaceBanners/MarketplaceBanners.tsx
rename to autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/other/MarketplaceBanners.tsx
index 4f826f6e85..00edcc721f 100644
--- a/autogpt_platform/frontend/src/components/contextual/MarketplaceBanners/MarketplaceBanners.tsx
+++ b/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/other/MarketplaceBanners.tsx
@@ -3,7 +3,7 @@
import { Button } from "@/components/atoms/Button/Button";
import { Text } from "@/components/atoms/Text/Text";
-interface MarketplaceBannersProps {
+interface Props {
hasUpdate?: boolean;
latestVersion?: number;
hasUnpublishedChanges?: boolean;
@@ -21,7 +21,7 @@ export function MarketplaceBanners({
isUpdating,
onUpdate,
onPublish,
-}: MarketplaceBannersProps) {
+}: Props) {
const renderUpdateBanner = () => {
if (hasUpdate && latestVersion) {
return (
diff --git a/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/other/SectionWrap.tsx b/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/other/SectionWrap.tsx
index 75571dd856..f88d91bb0d 100644
--- a/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/other/SectionWrap.tsx
+++ b/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/other/SectionWrap.tsx
@@ -1,3 +1,5 @@
+"use client";
+
import { cn } from "@/lib/utils";
type Props = {
diff --git a/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/selected-views/LoadingSelectedContent.tsx b/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/selected-views/LoadingSelectedContent.tsx
index dc2bb7cac2..bc5548afd0 100644
--- a/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/selected-views/LoadingSelectedContent.tsx
+++ b/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/selected-views/LoadingSelectedContent.tsx
@@ -1,22 +1,16 @@
+import { LibraryAgent } from "@/app/api/__generated__/models/libraryAgent";
import { Skeleton } from "@/components/__legacy__/ui/skeleton";
import { cn } from "@/lib/utils";
-import { LibraryAgent } from "@/app/api/__generated__/models/libraryAgent";
import { AGENT_LIBRARY_SECTION_PADDING_X } from "../../helpers";
import { SelectedViewLayout } from "./SelectedViewLayout";
interface Props {
agent: LibraryAgent;
- onSelectSettings?: () => void;
- selectedSettings?: boolean;
}
export function LoadingSelectedContent(props: Props) {
return (
-
+
diff --git a/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/selected-views/SelectedRunView/SelectedRunView.tsx b/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/selected-views/SelectedRunView/SelectedRunView.tsx
index c66f0e9245..05da986583 100644
--- a/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/selected-views/SelectedRunView/SelectedRunView.tsx
+++ b/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/selected-views/SelectedRunView/SelectedRunView.tsx
@@ -33,8 +33,6 @@ interface Props {
onSelectRun?: (id: string) => void;
onClearSelectedRun?: () => void;
banner?: React.ReactNode;
- onSelectSettings?: () => void;
- selectedSettings?: boolean;
}
export function SelectedRunView({
@@ -43,8 +41,6 @@ export function SelectedRunView({
onSelectRun,
onClearSelectedRun,
banner,
- onSelectSettings,
- selectedSettings,
}: Props) {
const { run, preset, isLoading, responseError, httpError } =
useSelectedRunView(agent.graph_id, runId);
@@ -84,12 +80,7 @@ export function SelectedRunView({
return (
-
+
diff --git a/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/selected-views/SelectedScheduleView/SelectedScheduleView.tsx b/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/selected-views/SelectedScheduleView/SelectedScheduleView.tsx
index 445394c44a..e0a81dba5f 100644
--- a/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/selected-views/SelectedScheduleView/SelectedScheduleView.tsx
+++ b/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/selected-views/SelectedScheduleView/SelectedScheduleView.tsx
@@ -21,8 +21,6 @@ interface Props {
scheduleId: string;
onClearSelectedRun?: () => void;
banner?: React.ReactNode;
- onSelectSettings?: () => void;
- selectedSettings?: boolean;
}
export function SelectedScheduleView({
@@ -30,8 +28,6 @@ export function SelectedScheduleView({
scheduleId,
onClearSelectedRun,
banner,
- onSelectSettings,
- selectedSettings,
}: Props) {
const { schedule, isLoading, error } = useSelectedScheduleView(
agent.graph_id,
@@ -76,12 +72,7 @@ export function SelectedScheduleView({
return (
-
+
{}}>
+
Agent Settings
-
- {!hasHITLBlocks ? (
-
-
- This agent doesn't have any human-in-the-loop blocks, so
- there are no settings to configure.
-
-
- ) : (
+
+ {hasHITLBlocks ? (
@@ -59,6 +52,12 @@ export function SelectedSettingsView({ agent, onClearSelectedRun }: Props) {
/>
+ ) : (
+
+
+ This agent doesn't have any configurable settings.
+
+
)}
diff --git a/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/selected-views/SelectedTemplateView/SelectedTemplateView.tsx b/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/selected-views/SelectedTemplateView/SelectedTemplateView.tsx
index b5ecb7ae5c..d0c49c2a93 100644
--- a/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/selected-views/SelectedTemplateView/SelectedTemplateView.tsx
+++ b/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/selected-views/SelectedTemplateView/SelectedTemplateView.tsx
@@ -8,7 +8,7 @@ import {
getAgentCredentialsFields,
getAgentInputFields,
} from "../../modals/AgentInputsReadOnly/helpers";
-import { CredentialsInput } from "../../modals/CredentialsInputs/CredentialsInputs";
+import { CredentialsInput } from "../../modals/CredentialsInputs/CredentialsInput";
import { RunAgentInputs } from "../../modals/RunAgentInputs/RunAgentInputs";
import { LoadingSelectedContent } from "../LoadingSelectedContent";
import { RunDetailCard } from "../RunDetailCard/RunDetailCard";
diff --git a/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/selected-views/SelectedTriggerView/SelectedTriggerView.tsx b/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/selected-views/SelectedTriggerView/SelectedTriggerView.tsx
index f92c91112e..0d0cdc95cc 100644
--- a/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/selected-views/SelectedTriggerView/SelectedTriggerView.tsx
+++ b/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/selected-views/SelectedTriggerView/SelectedTriggerView.tsx
@@ -7,7 +7,7 @@ import {
getAgentCredentialsFields,
getAgentInputFields,
} from "../../modals/AgentInputsReadOnly/helpers";
-import { CredentialsInput } from "../../modals/CredentialsInputs/CredentialsInputs";
+import { CredentialsInput } from "../../modals/CredentialsInputs/CredentialsInput";
import { RunAgentInputs } from "../../modals/RunAgentInputs/RunAgentInputs";
import { LoadingSelectedContent } from "../LoadingSelectedContent";
import { RunDetailCard } from "../RunDetailCard/RunDetailCard";
diff --git a/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/selected-views/SelectedViewLayout.tsx b/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/selected-views/SelectedViewLayout.tsx
index 8a4e46a606..fe824604df 100644
--- a/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/selected-views/SelectedViewLayout.tsx
+++ b/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/selected-views/SelectedViewLayout.tsx
@@ -1,7 +1,7 @@
-import { Breadcrumbs } from "@/components/molecules/Breadcrumbs/Breadcrumbs";
-import { AgentSettingsButton } from "@/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/other/AgentSettingsButton";
import { LibraryAgent } from "@/app/api/__generated__/models/libraryAgent";
+import { Breadcrumbs } from "@/components/molecules/Breadcrumbs/Breadcrumbs";
import { AGENT_LIBRARY_SECTION_PADDING_X } from "../../helpers";
+import { AgentSettingsModal } from "../modals/AgentSettingsModal/AgentSettingsModal";
import { SectionWrap } from "../other/SectionWrap";
interface Props {
@@ -9,8 +9,6 @@ interface Props {
children: React.ReactNode;
banner?: React.ReactNode;
additionalBreadcrumb?: { name: string; link?: string };
- onSelectSettings?: () => void;
- selectedSettings?: boolean;
}
export function SelectedViewLayout(props: Props) {
@@ -19,8 +17,8 @@ export function SelectedViewLayout(props: Props) {
- {props.banner &&
{props.banner}
}
-
+ {props.banner}
+
- {props.agent && props.onSelectSettings && (
-
- )}
+
diff --git a/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/OldAgentLibraryView/components/agent-run-draft-view.tsx b/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/OldAgentLibraryView/components/agent-run-draft-view.tsx
index 5f57032618..1b155543f1 100644
--- a/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/OldAgentLibraryView/components/agent-run-draft-view.tsx
+++ b/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/OldAgentLibraryView/components/agent-run-draft-view.tsx
@@ -12,7 +12,7 @@ import {
} from "@/lib/autogpt-server-api";
import { useBackendAPI } from "@/lib/autogpt-server-api/context";
-import { CredentialsInput } from "@/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/modals/CredentialsInputs/CredentialsInputs";
+import { CredentialsInput } from "@/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/modals/CredentialsInputs/CredentialsInput";
import { RunAgentInputs } from "@/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/modals/RunAgentInputs/RunAgentInputs";
import { ScheduleTaskDialog } from "@/app/(platform)/library/agents/[id]/components/OldAgentLibraryView/components/cron-scheduler-dialog";
import ActionButtonGroup from "@/components/__legacy__/action-button-group";
diff --git a/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/page.tsx b/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/page.tsx
index 9ada590dd8..147c0aef45 100644
--- a/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/page.tsx
+++ b/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/page.tsx
@@ -1,14 +1,7 @@
"use client";
-import { Flag, useGetFlag } from "@/services/feature-flags/use-get-flag";
import { NewAgentLibraryView } from "./components/NewAgentLibraryView/NewAgentLibraryView";
-import { OldAgentLibraryView } from "./components/OldAgentLibraryView/OldAgentLibraryView";
export default function AgentLibraryPage() {
- const isNewLibraryPageEnabled = useGetFlag(Flag.NEW_AGENT_RUNS);
- return isNewLibraryPageEnabled ? (
-
- ) : (
-
- );
+ return
;
}
diff --git a/autogpt_platform/frontend/src/app/api/openapi.json b/autogpt_platform/frontend/src/app/api/openapi.json
index e601be6626..6f9a87216b 100644
--- a/autogpt_platform/frontend/src/app/api/openapi.json
+++ b/autogpt_platform/frontend/src/app/api/openapi.json
@@ -2870,6 +2870,28 @@
}
}
},
+ "/api/integrations/providers/system": {
+ "get": {
+ "tags": ["v1", "integrations"],
+ "summary": "List System Providers",
+ "description": "Get a list of providers that have platform credits (system credentials) available.\n\nThese providers can be used without the user providing their own API keys.",
+ "operationId": "getV1ListSystemProviders",
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "application/json": {
+ "schema": {
+ "items": { "type": "string" },
+ "type": "array",
+ "title": "Response Getv1Listsystemproviders"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
"/api/integrations/webhooks/{webhook_id}/ping": {
"post": {
"tags": ["v1", "integrations"],
diff --git a/autogpt_platform/frontend/src/components/atoms/Button/Button.tsx b/autogpt_platform/frontend/src/components/atoms/Button/Button.tsx
index de1dec2d25..ab7b90e098 100644
--- a/autogpt_platform/frontend/src/components/atoms/Button/Button.tsx
+++ b/autogpt_platform/frontend/src/components/atoms/Button/Button.tsx
@@ -20,6 +20,7 @@ export function Button(props: ButtonProps) {
rightIcon,
children,
as = "button",
+ asChild: _asChild, // Destructure to prevent passing to DOM
...restProps
} = props;
diff --git a/autogpt_platform/frontend/src/components/contextual/GoogleDrivePicker/GoogleDrivePicker.tsx b/autogpt_platform/frontend/src/components/contextual/GoogleDrivePicker/GoogleDrivePicker.tsx
index e0a43b8c77..4decd2dbdb 100644
--- a/autogpt_platform/frontend/src/components/contextual/GoogleDrivePicker/GoogleDrivePicker.tsx
+++ b/autogpt_platform/frontend/src/components/contextual/GoogleDrivePicker/GoogleDrivePicker.tsx
@@ -1,6 +1,6 @@
"use client";
-import { CredentialsInput } from "@/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/modals/CredentialsInputs/CredentialsInputs";
+import { CredentialsInput } from "@/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/modals/CredentialsInputs/CredentialsInput";
import { Button } from "@/components/atoms/Button/Button";
import { CircleNotchIcon, FolderOpenIcon } from "@phosphor-icons/react";
import {
diff --git a/autogpt_platform/frontend/src/components/molecules/Accordion/Accordion.stories.tsx b/autogpt_platform/frontend/src/components/molecules/Accordion/Accordion.stories.tsx
new file mode 100644
index 0000000000..d0fce53e0e
--- /dev/null
+++ b/autogpt_platform/frontend/src/components/molecules/Accordion/Accordion.stories.tsx
@@ -0,0 +1,203 @@
+import type { Meta } from "@storybook/nextjs";
+import {
+ Accordion,
+ AccordionContent,
+ AccordionItem,
+ AccordionTrigger,
+} from "./Accordion";
+
+const meta: Meta
= {
+ title: "Molecules/Accordion",
+ component: Accordion,
+ parameters: {
+ layout: "centered",
+ docs: {
+ description: {
+ component: `
+## Accordion Component
+
+A vertically stacked set of interactive headings that each reveal an associated section of content.
+
+### β¨ Features
+
+- **Built on Radix UI** - Uses @radix-ui/react-accordion for accessibility and functionality
+- **Single or multiple** - Supports single or multiple items open at once
+- **Smooth animations** - Built-in expand/collapse animations
+- **Accessible** - Full keyboard navigation and screen reader support
+- **Customizable** - Style with Tailwind CSS classes
+
+### π― Usage
+
+\`\`\`tsx
+
+
+ Is it accessible?
+
+ Yes. It adheres to the WAI-ARIA design pattern.
+
+
+
+\`\`\`
+
+### Props
+
+**Accordion**:
+- **type**: "single" | "multiple" - Whether one or multiple items can be open
+- **collapsible**: boolean - When type is "single", allows closing all items
+- **defaultValue**: string | string[] - Default open item(s)
+- **value**: string | string[] - Controlled open item(s)
+- **onValueChange**: (value) => void - Callback when value changes
+
+**AccordionItem**:
+- **value**: string - Unique identifier for the item
+- **disabled**: boolean - Whether the item is disabled
+
+**AccordionTrigger**:
+- Standard button props
+
+**AccordionContent**:
+- Standard div props
+ `,
+ },
+ },
+ },
+ tags: ["autodocs"],
+ argTypes: {
+ type: {
+ control: "radio",
+ options: ["single", "multiple"],
+ description: "Whether one or multiple items can be open at the same time",
+ table: {
+ defaultValue: { summary: "single" },
+ },
+ },
+ collapsible: {
+ control: "boolean",
+ description:
+ 'When type is "single", allows closing content when clicking on open trigger',
+ table: {
+ defaultValue: { summary: "false" },
+ },
+ },
+ },
+};
+
+export default meta;
+
+export function Default() {
+ return (
+
+
+ Is it accessible?
+
+ Yes. It adheres to the WAI-ARIA design pattern.
+
+
+
+ Is it styled?
+
+ Yes. It comes with default styles that match your design system.
+
+
+
+ Is it animated?
+
+ Yes. It's animated by default with smooth expand/collapse
+ transitions.
+
+
+
+ );
+}
+
+export function Multiple() {
+ return (
+
+
+ First section
+
+ Multiple items can be open at the same time when type is set to
+ "multiple".
+
+
+
+ Second section
+
+ Try opening this one while the first is still open.
+
+
+
+ Third section
+
+ All three can be open simultaneously.
+
+
+
+ );
+}
+
+export function DefaultOpen() {
+ return (
+
+
+ Closed by default
+ This item starts closed.
+
+
+ Open by default
+
+ This item starts open because defaultValue is set to
+ "item-2".
+
+
+
+ Also closed
+ This item also starts closed.
+
+
+ );
+}
+
+export function WithDisabledItem() {
+ return (
+
+
+ Available item
+ This item can be toggled.
+
+
+ Disabled item
+
+ This content cannot be accessed because the item is disabled.
+
+
+
+ Another available item
+ This item can also be toggled.
+
+
+ );
+}
+
+export function CustomStyled() {
+ return (
+
+
+
+ Custom styled trigger
+
+
+ You can customize the styling using className props.
+
+
+
+
+ Blue themed
+
+
+ Each item can have different styles.
+
+
+
+ );
+}
diff --git a/autogpt_platform/frontend/src/components/molecules/Accordion/Accordion.tsx b/autogpt_platform/frontend/src/components/molecules/Accordion/Accordion.tsx
new file mode 100644
index 0000000000..b071fc1d37
--- /dev/null
+++ b/autogpt_platform/frontend/src/components/molecules/Accordion/Accordion.tsx
@@ -0,0 +1,8 @@
+"use client";
+
+export {
+ Accordion,
+ AccordionContent,
+ AccordionItem,
+ AccordionTrigger,
+} from "@/components/ui/accordion";
diff --git a/autogpt_platform/frontend/src/components/molecules/Dialog/components/DrawerWrap.tsx b/autogpt_platform/frontend/src/components/molecules/Dialog/components/DrawerWrap.tsx
index d00817bf59..3bfa321538 100644
--- a/autogpt_platform/frontend/src/components/molecules/Dialog/components/DrawerWrap.tsx
+++ b/autogpt_platform/frontend/src/components/molecules/Dialog/components/DrawerWrap.tsx
@@ -22,6 +22,9 @@ export function DrawerWrap({
handleClose,
isForceOpen,
}: Props) {
+ const accessibleTitle = title ?? "Dialog";
+ const hasVisibleTitle = Boolean(title);
+
const closeBtn = (
- {title ? (
-
{title}
- ) : null}
+ {hasVisibleTitle ? (
+
+ {accessibleTitle}
+
+ ) : (
+
{accessibleTitle}
+ )}
{!isForceOpen ? (
- title ? (
+ hasVisibleTitle ? (
closeBtn
) : (
{
const { formData, onChange, schema, registry, fieldPathId, required } = props;
diff --git a/autogpt_platform/frontend/src/components/ui/accordion.tsx b/autogpt_platform/frontend/src/components/ui/accordion.tsx
new file mode 100644
index 0000000000..47216c6d9e
--- /dev/null
+++ b/autogpt_platform/frontend/src/components/ui/accordion.tsx
@@ -0,0 +1,57 @@
+"use client";
+
+import * as AccordionPrimitive from "@radix-ui/react-accordion";
+import { ChevronDown } from "lucide-react";
+import * as React from "react";
+
+import { cn } from "@/lib/utils";
+
+const Accordion = AccordionPrimitive.Root;
+
+const AccordionItem = React.forwardRef<
+ React.ElementRef
,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+));
+AccordionItem.displayName = "AccordionItem";
+
+const AccordionTrigger = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, children, ...props }, ref) => (
+
+ svg]:rotate-180",
+ className,
+ )}
+ {...props}
+ >
+ {children}
+
+
+
+));
+AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName;
+
+const AccordionContent = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, children, ...props }, ref) => (
+
+ {children}
+
+));
+AccordionContent.displayName = AccordionPrimitive.Content.displayName;
+
+export { Accordion, AccordionContent, AccordionItem, AccordionTrigger };
diff --git a/autogpt_platform/frontend/src/lib/autogpt-server-api/client.ts b/autogpt_platform/frontend/src/lib/autogpt-server-api/client.ts
index 682fc14108..e58b5f6020 100644
--- a/autogpt_platform/frontend/src/lib/autogpt-server-api/client.ts
+++ b/autogpt_platform/frontend/src/lib/autogpt-server-api/client.ts
@@ -352,6 +352,10 @@ export default class BackendAPI {
return this._get("/integrations/providers");
}
+ listSystemProviders(): Promise {
+ return this._get("/integrations/providers/system");
+ }
+
listCredentials(provider?: string): Promise {
return this._get(
provider
diff --git a/autogpt_platform/frontend/src/lib/autogpt-server-api/types.ts b/autogpt_platform/frontend/src/lib/autogpt-server-api/types.ts
index 06f62d5fd4..8bc39a390a 100644
--- a/autogpt_platform/frontend/src/lib/autogpt-server-api/types.ts
+++ b/autogpt_platform/frontend/src/lib/autogpt-server-api/types.ts
@@ -625,6 +625,7 @@ export type CredentialsMetaResponse = {
scopes?: Array;
username?: string;
host?: string;
+ is_system?: boolean;
};
/* Mirror of backend/server/integrations/router.py:CredentialsDeletionResponse */
diff --git a/autogpt_platform/frontend/src/providers/agent-credentials/credentials-provider.tsx b/autogpt_platform/frontend/src/providers/agent-credentials/credentials-provider.tsx
index bf6b91205d..e47cc65e13 100644
--- a/autogpt_platform/frontend/src/providers/agent-credentials/credentials-provider.tsx
+++ b/autogpt_platform/frontend/src/providers/agent-credentials/credentials-provider.tsx
@@ -1,5 +1,4 @@
-import { createContext, useCallback, useEffect, useState } from "react";
-import { useSupabase } from "@/lib/supabase/hooks/useSupabase";
+import { useToastOnFail } from "@/components/molecules/Toast/use-toast";
import {
APIKeyCredentials,
CredentialsDeleteNeedConfirmationResponse,
@@ -10,8 +9,9 @@ import {
UserPasswordCredentials,
} from "@/lib/autogpt-server-api";
import { useBackendAPI } from "@/lib/autogpt-server-api/context";
-import { useToastOnFail } from "@/components/molecules/Toast/use-toast";
+import { useSupabase } from "@/lib/supabase/hooks/useSupabase";
import { toDisplayName } from "@/providers/agent-credentials/helper";
+import { createContext, useCallback, useEffect, useState } from "react";
type APIKeyCredentialsCreatable = Omit<
APIKeyCredentials,
@@ -32,6 +32,8 @@ export type CredentialsProviderData = {
provider: CredentialsProviderName;
providerName: string;
savedCredentials: CredentialsMetaResponse[];
+ /** Whether this provider has platform credits available (system credentials) */
+ isSystemProvider: boolean;
oAuthCallback: (
code: string,
state_token: string,
@@ -68,6 +70,9 @@ export default function CredentialsProvider({
const [providers, setProviders] =
useState(null);
const [providerNames, setProviderNames] = useState([]);
+ const [systemProviders, setSystemProviders] = useState>(
+ new Set(),
+ );
const { isLoggedIn } = useSupabase();
const api = useBackendAPI();
const onFailToast = useToastOnFail();
@@ -218,17 +223,7 @@ export default function CredentialsProvider({
[api, onFailToast],
);
- // Fetch provider names on mount
- useEffect(() => {
- api
- .listProviders()
- .then((names) => {
- setProviderNames(names);
- })
- .catch(onFailToast("load provider names"));
- }, [api, onFailToast]);
-
- useEffect(() => {
+ const loadCredentials = useCallback(() => {
if (!isLoggedIn || providerNames.length === 0) {
if (isLoggedIn == false) setProviders({});
return;
@@ -251,27 +246,32 @@ export default function CredentialsProvider({
setProviders((prev) => ({
...prev,
...Object.fromEntries(
- providerNames.map((provider) => [
- provider,
- {
+ providerNames.map((provider) => {
+ const providerCredentials = credentialsByProvider[provider] ?? [];
+
+ return [
provider,
- providerName: toDisplayName(provider as string),
- savedCredentials: credentialsByProvider[provider] ?? [],
- oAuthCallback: (code: string, state_token: string) =>
- oAuthCallback(provider, code, state_token),
- createAPIKeyCredentials: (
- credentials: APIKeyCredentialsCreatable,
- ) => createAPIKeyCredentials(provider, credentials),
- createUserPasswordCredentials: (
- credentials: UserPasswordCredentialsCreatable,
- ) => createUserPasswordCredentials(provider, credentials),
- createHostScopedCredentials: (
- credentials: HostScopedCredentialsCreatable,
- ) => createHostScopedCredentials(provider, credentials),
- deleteCredentials: (id: string, force: boolean = false) =>
- deleteCredentials(provider, id, force),
- } satisfies CredentialsProviderData,
- ]),
+ {
+ provider,
+ providerName: toDisplayName(provider as string),
+ savedCredentials: providerCredentials,
+ isSystemProvider: systemProviders.has(provider),
+ oAuthCallback: (code: string, state_token: string) =>
+ oAuthCallback(provider, code, state_token),
+ createAPIKeyCredentials: (
+ credentials: APIKeyCredentialsCreatable,
+ ) => createAPIKeyCredentials(provider, credentials),
+ createUserPasswordCredentials: (
+ credentials: UserPasswordCredentialsCreatable,
+ ) => createUserPasswordCredentials(provider, credentials),
+ createHostScopedCredentials: (
+ credentials: HostScopedCredentialsCreatable,
+ ) => createHostScopedCredentials(provider, credentials),
+ deleteCredentials: (id: string, force: boolean = false) =>
+ deleteCredentials(provider, id, force),
+ } satisfies CredentialsProviderData,
+ ];
+ }),
),
}));
})
@@ -280,6 +280,7 @@ export default function CredentialsProvider({
api,
isLoggedIn,
providerNames,
+ systemProviders,
createAPIKeyCredentials,
createUserPasswordCredentials,
createHostScopedCredentials,
@@ -288,6 +289,20 @@ export default function CredentialsProvider({
onFailToast,
]);
+ // Fetch provider names and system providers on mount
+ useEffect(() => {
+ Promise.all([api.listProviders(), api.listSystemProviders()])
+ .then(([names, systemList]) => {
+ setProviderNames(names);
+ setSystemProviders(new Set(systemList));
+ })
+ .catch(onFailToast("Load provider names"));
+ }, [api, onFailToast]);
+
+ useEffect(() => {
+ loadCredentials();
+ }, [loadCredentials]);
+
return (
{children}
diff --git a/autogpt_platform/frontend/src/tests/agent-activity.spec.ts b/autogpt_platform/frontend/src/tests/agent-activity.spec.ts
index 02956fe4e9..e91fceb35a 100644
--- a/autogpt_platform/frontend/src/tests/agent-activity.spec.ts
+++ b/autogpt_platform/frontend/src/tests/agent-activity.spec.ts
@@ -1,17 +1,17 @@
import test, { expect } from "@playwright/test";
-import { hasTextContent, hasUrl, isVisible } from "./utils/assertion";
-import { getSelectors } from "./utils/selectors";
-import { getTestUser } from "./utils/auth";
-import { LoginPage } from "./pages/login.page";
import { BuildPage } from "./pages/build.page";
import * as LibraryPage from "./pages/library.page";
+import { LoginPage } from "./pages/login.page";
+import { hasTextContent, hasUrl, isVisible } from "./utils/assertion";
+import { getTestUser } from "./utils/auth";
+import { getSelectors } from "./utils/selectors";
test.beforeEach(async ({ page }) => {
const loginPage = new LoginPage(page);
const buildPage = new BuildPage(page);
const testUser = await getTestUser();
- const { getId, getText } = getSelectors(page);
+ const { getId } = getSelectors(page);
await page.goto("/login");
await loginPage.login(testUser.email, testUser.password);
@@ -41,7 +41,8 @@ test.beforeEach(async ({ page }) => {
await page.goto("/library");
await LibraryPage.clickFirstAgent(page);
await LibraryPage.waitForAgentPageLoad(page);
- await isVisible(getText("Test Agent"), 8000);
+ const { getRole } = getSelectors(page);
+ await isVisible(getRole("heading", "Test Agent"), 8000);
});
test("shows badge with count when agent is running", async ({ page }) => {
diff --git a/autogpt_platform/frontend/src/tests/agent-dashboard.spec.ts b/autogpt_platform/frontend/src/tests/agent-dashboard.spec.ts
index ea1629f929..fe93e7c258 100644
--- a/autogpt_platform/frontend/src/tests/agent-dashboard.spec.ts
+++ b/autogpt_platform/frontend/src/tests/agent-dashboard.spec.ts
@@ -1,13 +1,14 @@
-import { LoginPage } from "./pages/login.page";
import test, { expect } from "@playwright/test";
-import { TEST_CREDENTIALS } from "./credentials";
-import { getSelectors } from "./utils/selectors";
+import { getTestUserWithLibraryAgents } from "./credentials";
+import { LoginPage } from "./pages/login.page";
import { hasUrl, isHidden } from "./utils/assertion";
+import { getSelectors } from "./utils/selectors";
test.beforeEach(async ({ page }) => {
const loginPage = new LoginPage(page);
await page.goto("/login");
- await loginPage.login(TEST_CREDENTIALS.email, TEST_CREDENTIALS.password);
+ const richUser = getTestUserWithLibraryAgents();
+ await loginPage.login(richUser.email, richUser.password);
await hasUrl(page, "/marketplace");
});
diff --git a/autogpt_platform/frontend/src/tests/api-keys.spec.ts b/autogpt_platform/frontend/src/tests/api-keys.spec.ts
index e2a5575aed..813272ce5e 100644
--- a/autogpt_platform/frontend/src/tests/api-keys.spec.ts
+++ b/autogpt_platform/frontend/src/tests/api-keys.spec.ts
@@ -1,6 +1,6 @@
import { expect, test } from "@playwright/test";
+import { getTestUserWithLibraryAgents } from "./credentials";
import { LoginPage } from "./pages/login.page";
-import { TEST_CREDENTIALS } from "./credentials";
import { hasUrl } from "./utils/assertion";
import { getSelectors } from "./utils/selectors";
@@ -8,7 +8,8 @@ test.describe("API Keys Page", () => {
test.beforeEach(async ({ page }) => {
const loginPage = new LoginPage(page);
await page.goto("/login");
- await loginPage.login(TEST_CREDENTIALS.email, TEST_CREDENTIALS.password);
+ const richUser = getTestUserWithLibraryAgents();
+ await loginPage.login(richUser.email, richUser.password);
await hasUrl(page, "/marketplace");
});
diff --git a/autogpt_platform/frontend/src/tests/credentials/index.ts b/autogpt_platform/frontend/src/tests/credentials/index.ts
index ebf7528b63..bc4663a045 100644
--- a/autogpt_platform/frontend/src/tests/credentials/index.ts
+++ b/autogpt_platform/frontend/src/tests/credentials/index.ts
@@ -4,6 +4,10 @@ export const TEST_CREDENTIALS = {
password: "testpassword123",
} as const;
+export function getTestUserWithLibraryAgents() {
+ return TEST_CREDENTIALS;
+}
+
// Dummy constant to help developers identify agents that don't need input
export const DummyInput = "DummyInput";
diff --git a/autogpt_platform/frontend/src/tests/library.spec.ts b/autogpt_platform/frontend/src/tests/library.spec.ts
index 57717dd8a7..1972e94522 100644
--- a/autogpt_platform/frontend/src/tests/library.spec.ts
+++ b/autogpt_platform/frontend/src/tests/library.spec.ts
@@ -1,6 +1,6 @@
import test, { expect } from "@playwright/test";
import path from "path";
-import { TEST_CREDENTIALS } from "./credentials";
+import { getTestUserWithLibraryAgents } from "./credentials";
import { LibraryPage } from "./pages/library.page";
import { LoginPage } from "./pages/login.page";
import { hasUrl } from "./utils/assertion";
@@ -14,7 +14,8 @@ test.describe("Library", () => {
await page.goto("/login");
const loginPage = new LoginPage(page);
- await loginPage.login(TEST_CREDENTIALS.email, TEST_CREDENTIALS.password);
+ const richUser = getTestUserWithLibraryAgents();
+ await loginPage.login(richUser.email, richUser.password);
await hasUrl(page, "/marketplace");
});
diff --git a/autogpt_platform/frontend/src/tests/marketplace-agent.spec.ts b/autogpt_platform/frontend/src/tests/marketplace-agent.spec.ts
index 9964d58e60..75efb134d5 100644
--- a/autogpt_platform/frontend/src/tests/marketplace-agent.spec.ts
+++ b/autogpt_platform/frontend/src/tests/marketplace-agent.spec.ts
@@ -1,10 +1,14 @@
-import { test, expect } from "@playwright/test";
-import { MarketplacePage } from "./pages/marketplace.page";
+import { expect, test } from "@playwright/test";
+import { getTestUserWithLibraryAgents } from "./credentials";
import { LoginPage } from "./pages/login.page";
-import { isVisible, hasUrl, matchesUrl } from "./utils/assertion";
-import { TEST_CREDENTIALS } from "./credentials";
+import { MarketplacePage } from "./pages/marketplace.page";
+import { hasUrl, isVisible, matchesUrl } from "./utils/assertion";
import { getSelectors } from "./utils/selectors";
+function escapeRegExp(value: string) {
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
+}
+
test.describe("Marketplace Agent Page - Basic Functionality", () => {
test("User can access agent page when logged out", async ({ page }) => {
const marketplacePage = new MarketplacePage(page);
@@ -24,7 +28,8 @@ test.describe("Marketplace Agent Page - Basic Functionality", () => {
const marketplacePage = new MarketplacePage(page);
await loginPage.goto();
- await loginPage.login(TEST_CREDENTIALS.email, TEST_CREDENTIALS.password);
+ const richUser = getTestUserWithLibraryAgents();
+ await loginPage.login(richUser.email, richUser.password);
await hasUrl(page, "/marketplace");
await marketplacePage.goto(page);
await hasUrl(page, "/marketplace");
@@ -85,7 +90,8 @@ test.describe("Marketplace Agent Page - Basic Functionality", () => {
const marketplacePage = new MarketplacePage(page);
await loginPage.goto();
- await loginPage.login(TEST_CREDENTIALS.email, TEST_CREDENTIALS.password);
+ const richUser = getTestUserWithLibraryAgents();
+ await loginPage.login(richUser.email, richUser.password);
await hasUrl(page, "/marketplace");
await marketplacePage.goto(page);
@@ -93,6 +99,12 @@ test.describe("Marketplace Agent Page - Basic Functionality", () => {
await firstStoreCard.click();
await page.waitForURL("**/marketplace/agent/**");
+ const agentTitle = await getId("agent-title").textContent();
+ if (!agentTitle || !agentTitle.trim()) {
+ throw new Error("Agent title not found on marketplace agent page");
+ }
+ const agentName = agentTitle.trim();
+
const addToLibraryButton = getId("agent-add-library-button");
await isVisible(addToLibraryButton);
await addToLibraryButton.click();
@@ -101,9 +113,8 @@ test.describe("Marketplace Agent Page - Basic Functionality", () => {
await isVisible(addSuccessMessage);
await page.waitForURL("**/library/agents/**");
- const agentNameOnLibrary = await getId("agent-title").textContent();
- expect(
- agentNameOnLibrary && agentNameOnLibrary.trim().length,
- ).toBeGreaterThan(0);
+ await expect(page).toHaveTitle(
+ new RegExp(`${escapeRegExp(agentName)} - Library - AutoGPT Platform`),
+ );
});
});
diff --git a/autogpt_platform/frontend/src/tests/marketplace-creator.spec.ts b/autogpt_platform/frontend/src/tests/marketplace-creator.spec.ts
index 8b3e2e1e64..3558f0672c 100644
--- a/autogpt_platform/frontend/src/tests/marketplace-creator.spec.ts
+++ b/autogpt_platform/frontend/src/tests/marketplace-creator.spec.ts
@@ -1,8 +1,8 @@
import { test } from "@playwright/test";
-import { MarketplacePage } from "./pages/marketplace.page";
+import { getTestUserWithLibraryAgents } from "./credentials";
import { LoginPage } from "./pages/login.page";
-import { isVisible, hasUrl, matchesUrl } from "./utils/assertion";
-import { TEST_CREDENTIALS } from "./credentials";
+import { MarketplacePage } from "./pages/marketplace.page";
+import { hasUrl, isVisible, matchesUrl } from "./utils/assertion";
import { getSelectors } from "./utils/selectors";
test.describe("Marketplace Creator Page β Basic Functionality", () => {
@@ -25,7 +25,8 @@ test.describe("Marketplace Creator Page β Basic Functionality", () => {
const marketplacePage = new MarketplacePage(page);
await loginPage.goto();
- await loginPage.login(TEST_CREDENTIALS.email, TEST_CREDENTIALS.password);
+ const richUser = getTestUserWithLibraryAgents();
+ await loginPage.login(richUser.email, richUser.password);
await hasUrl(page, "/marketplace");
await marketplacePage.goto(page);
diff --git a/autogpt_platform/frontend/src/tests/marketplace.spec.ts b/autogpt_platform/frontend/src/tests/marketplace.spec.ts
index 8bbc1cef90..6f06f7addb 100644
--- a/autogpt_platform/frontend/src/tests/marketplace.spec.ts
+++ b/autogpt_platform/frontend/src/tests/marketplace.spec.ts
@@ -1,8 +1,8 @@
-import { test, expect } from "@playwright/test";
-import { MarketplacePage } from "./pages/marketplace.page";
+import { expect, test } from "@playwright/test";
+import { getTestUserWithLibraryAgents } from "./credentials";
import { LoginPage } from "./pages/login.page";
-import { isVisible, hasUrl, hasMinCount, matchesUrl } from "./utils/assertion";
-import { TEST_CREDENTIALS } from "./credentials";
+import { MarketplacePage } from "./pages/marketplace.page";
+import { hasMinCount, hasUrl, isVisible, matchesUrl } from "./utils/assertion";
test.describe("Marketplace β Basic Functionality", () => {
test("User can access marketplace page when logged out", async ({ page }) => {
@@ -24,7 +24,8 @@ test.describe("Marketplace β Basic Functionality", () => {
const marketplacePage = new MarketplacePage(page);
await loginPage.goto();
- await loginPage.login(TEST_CREDENTIALS.email, TEST_CREDENTIALS.password);
+ const richUser = getTestUserWithLibraryAgents();
+ await loginPage.login(richUser.email, richUser.password);
await hasUrl(page, "/marketplace");
await marketplacePage.goto(page);
diff --git a/autogpt_platform/frontend/src/tests/pages/library.page.ts b/autogpt_platform/frontend/src/tests/pages/library.page.ts
index e108109c12..b9af750990 100644
--- a/autogpt_platform/frontend/src/tests/pages/library.page.ts
+++ b/autogpt_platform/frontend/src/tests/pages/library.page.ts
@@ -455,6 +455,30 @@ export async function navigateToAgentByName(
export async function clickRunButton(page: Page): Promise {
const { getId } = getSelectors(page);
+
+ const setupTaskButton = page.getByRole("button", {
+ name: /Setup your task/i,
+ });
+
+ if (await setupTaskButton.isVisible()) {
+ await setupTaskButton.click();
+ const startTaskButton = page
+ .getByRole("button", { name: /Start Task/i })
+ .first();
+ await startTaskButton.click();
+ return;
+ }
+
+ const newTaskButton = page.getByRole("button", { name: /New task/i });
+ if (await newTaskButton.isVisible()) {
+ await newTaskButton.click();
+ const startTaskButton = page
+ .getByRole("button", { name: /Start Task/i })
+ .first();
+ await startTaskButton.click();
+ return;
+ }
+
const runButton = getId("agent-run-button");
const runAgainButton = getId("run-again-button");
@@ -463,7 +487,7 @@ export async function clickRunButton(page: Page): Promise {
} else if (await runAgainButton.isVisible()) {
await runAgainButton.click();
} else {
- throw new Error("Neither run button nor run again button is visible");
+ throw new Error("Could not find run/start task button");
}
}
diff --git a/autogpt_platform/frontend/src/tests/profile-form.spec.ts b/autogpt_platform/frontend/src/tests/profile-form.spec.ts
index 1fc1008e9c..e9b018858f 100644
--- a/autogpt_platform/frontend/src/tests/profile-form.spec.ts
+++ b/autogpt_platform/frontend/src/tests/profile-form.spec.ts
@@ -1,8 +1,8 @@
import test, { expect } from "@playwright/test";
-import { ProfileFormPage } from "./pages/profile-form.page";
+import { getTestUserWithLibraryAgents } from "./credentials";
import { LoginPage } from "./pages/login.page";
+import { ProfileFormPage } from "./pages/profile-form.page";
import { hasUrl } from "./utils/assertion";
-import { TEST_CREDENTIALS } from "./credentials";
test.describe("Profile Form", () => {
let profileFormPage: ProfileFormPage;
@@ -12,7 +12,8 @@ test.describe("Profile Form", () => {
const loginPage = new LoginPage(page);
await loginPage.goto();
- await loginPage.login(TEST_CREDENTIALS.email, TEST_CREDENTIALS.password);
+ const richUser = getTestUserWithLibraryAgents();
+ await loginPage.login(richUser.email, richUser.password);
await hasUrl(page, "/marketplace");
});
diff --git a/autogpt_platform/frontend/src/tests/publish-agent.spec.ts b/autogpt_platform/frontend/src/tests/publish-agent.spec.ts
index 42cdee5536..6e1811e25d 100644
--- a/autogpt_platform/frontend/src/tests/publish-agent.spec.ts
+++ b/autogpt_platform/frontend/src/tests/publish-agent.spec.ts
@@ -1,7 +1,6 @@
import test from "@playwright/test";
+import { getTestUserWithLibraryAgents } from "./credentials";
import { LoginPage } from "./pages/login.page";
-import { TEST_CREDENTIALS } from "./credentials";
-import { getSelectors } from "./utils/selectors";
import {
hasUrl,
isDisabled,
@@ -9,6 +8,7 @@ import {
isHidden,
isVisible,
} from "./utils/assertion";
+import { getSelectors } from "./utils/selectors";
test("user can publish an agent through the complete flow", async ({
page,
@@ -17,7 +17,8 @@ test("user can publish an agent through the complete flow", async ({
const loginPage = new LoginPage(page);
await page.goto("/login");
- await loginPage.login(TEST_CREDENTIALS.email, TEST_CREDENTIALS.password);
+ const richUser = getTestUserWithLibraryAgents();
+ await loginPage.login(richUser.email, richUser.password);
await hasUrl(page, "/marketplace");
await page.goto("/marketplace");
@@ -95,7 +96,8 @@ test("should validate all form fields in publish agent form", async ({
const loginPage = new LoginPage(page);
await page.goto("/login");
- await loginPage.login(TEST_CREDENTIALS.email, TEST_CREDENTIALS.password);
+ const richUser = getTestUserWithLibraryAgents();
+ await loginPage.login(richUser.email, richUser.password);
await hasUrl(page, "/marketplace");
await page.goto("/marketplace");
diff --git a/autogpt_platform/frontend/tailwind.config.ts b/autogpt_platform/frontend/tailwind.config.ts
index ab3ea9bc74..f5a72e7916 100644
--- a/autogpt_platform/frontend/tailwind.config.ts
+++ b/autogpt_platform/frontend/tailwind.config.ts
@@ -22,13 +22,7 @@ const config = {
poppins: ["var(--font-poppins)"],
},
colors: {
- // *** APPROVED DESIGN SYSTEM COLORS ***
- // These are the ONLY colors that should be used in our app
...colors,
-
- // Legacy colors - DO NOT USE THESE IN NEW CODE
- // These are kept only to prevent breaking existing styles
- // Use the approved design system colors above instead
border: "hsl(var(--border))",
input: "hsl(var(--input))",
ring: "hsl(var(--ring))",
@@ -63,70 +57,66 @@ const config = {
foreground: "hsl(var(--card-foreground))",
},
customGray: {
- 100: "#d9d9d9",
- 200: "#a8a8a8",
- 300: "#878787",
- 400: "#646464",
- 500: "#474747",
- 600: "#282828",
- 700: "#272727",
+ "100": "#d9d9d9",
+ "200": "#a8a8a8",
+ "300": "#878787",
+ "400": "#646464",
+ "500": "#474747",
+ "600": "#282828",
+ "700": "#272727",
},
},
spacing: {
- // Tailwind spacing + custom sizes
- 0: "0rem", // 0px
- 0.5: "0.125rem", // 2px
- 1: "0.25rem", // 4px
- 1.5: "0.375rem", // 6px
- 2: "0.5rem", // 8px
- 2.5: "0.625rem", // 10px
- 3: "0.75rem", // 12px
- 3.5: "0.875rem", // 14px
- 4: "1rem", // 16px
- 5: "1.25rem", // 20px
- 6: "1.5rem", // 24px
- 7: "1.75rem", // 28px
- 7.5: "1.875rem", // 30px
- 8: "2rem", // 32px
- 8.5: "2.125rem", // 34px
- 9: "2.25rem", // 36px
- 10: "2.5rem", // 40px
- 11: "2.75rem", // 44px
- 12: "3rem", // 48px
- 14: "3.5rem", // 56px
- 16: "4rem", // 64px
- 18: "4.5rem", // 72px
- 20: "5rem", // 80px
- 24: "6rem", // 96px
- 28: "7rem", // 112px
- 32: "8rem", // 128px
- 36: "9rem", // 144px
- 40: "10rem", // 160px
- 44: "11rem", // 176px
- 48: "12rem", // 192px
- 52: "13rem", // 208px
- 56: "14rem", // 224px
- 60: "15rem", // 240px
- 64: "16rem", // 256px
- 68: "17rem", // 272px
- 70: "17.5rem", // 280px
- 71: "17.75rem", // 284px
- 72: "18rem", // 288px
- 76: "19rem", // 304px
- 80: "20rem", // 320px
- 96: "24rem", // 384px
+ "0": "0rem",
+ "1": "0.25rem",
+ "2": "0.5rem",
+ "3": "0.75rem",
+ "4": "1rem",
+ "5": "1.25rem",
+ "6": "1.5rem",
+ "7": "1.75rem",
+ "8": "2rem",
+ "9": "2.25rem",
+ "10": "2.5rem",
+ "11": "2.75rem",
+ "12": "3rem",
+ "14": "3.5rem",
+ "16": "4rem",
+ "18": "4.5rem",
+ "20": "5rem",
+ "24": "6rem",
+ "28": "7rem",
+ "32": "8rem",
+ "36": "9rem",
+ "40": "10rem",
+ "44": "11rem",
+ "48": "12rem",
+ "52": "13rem",
+ "56": "14rem",
+ "60": "15rem",
+ "64": "16rem",
+ "68": "17rem",
+ "70": "17.5rem",
+ "71": "17.75rem",
+ "72": "18rem",
+ "76": "19rem",
+ "80": "20rem",
+ "96": "24rem",
+ "0.5": "0.125rem",
+ "1.5": "0.375rem",
+ "2.5": "0.625rem",
+ "3.5": "0.875rem",
+ "7.5": "1.875rem",
+ "8.5": "2.125rem",
},
borderRadius: {
- // Design system border radius tokens from Figma
- xsmall: "0.25rem", // 4px
- small: "0.5rem", // 8px
- medium: "0.75rem", // 12px
- large: "1rem", // 16px
- xlarge: "1.25rem", // 20px
- "2xlarge": "1.5rem", // 24px
- full: "9999px", // For pill buttons
-
- // Legacy values - kept for backward compatibility
+ xsmall: "0.25rem",
+ small: "0.5rem",
+ medium: "0.75rem",
+ large: "1rem",
+ xlarge: "1.25rem",
+ "2xlarge": "1.5rem",
+ full: "9999px",
lg: "var(--radius)",
md: "calc(var(--radius) - 2px)",
sm: "calc(var(--radius) - 4px)",
@@ -136,16 +126,28 @@ const config = {
},
keyframes: {
"accordion-down": {
- from: { height: "0" },
- to: { height: "var(--radix-accordion-content-height)" },
+ from: {
+ height: "0",
+ },
+ to: {
+ height: "var(--radix-accordion-content-height)",
+ },
},
"accordion-up": {
- from: { height: "var(--radix-accordion-content-height)" },
- to: { height: "0" },
+ from: {
+ height: "var(--radix-accordion-content-height)",
+ },
+ to: {
+ height: "0",
+ },
},
"fade-in": {
- "0%": { opacity: "0" }, // Start with opacity 0
- "100%": { opacity: "1" }, // End with opacity 1
+ "0%": {
+ opacity: "0",
+ },
+ "100%": {
+ opacity: "1",
+ },
},
},
animation: {