Skip to content

fix(frontend): DataTable at phone widths, /m Tools and Triggers writable, and the shared drill-in bridge - #5893

Draft
ardaerzin wants to merge 16 commits into
pkg/settings-ee-pagesfrom
pkg/ui-data-table-responsive
Draft

fix(frontend): DataTable at phone widths, /m Tools and Triggers writable, and the shared drill-in bridge#5893
ardaerzin wants to merge 16 commits into
pkg/settings-ee-pagesfrom
pkg/ui-data-table-responsive

Conversation

@ardaerzin

Copy link
Copy Markdown
Contributor

The top of the stack — what was left after the settings pages were all in packages.

  • DataTable at phone widths: actions sit on the title's row, headers work when narrow, and a
    reload keeps the title on one line.
  • /m Tools and Triggers become writable (the form-engine lane below is what allows it), and /m
    actually reads the tools gate rather than assuming it.
  • useWorkflowReferenceBridge moves out of the OSS drill-in provider into @agenta/entity-ui, and
    /m's config pane runs on it.
  • atomFamily comes from jotai-family and the packages declare the dependency; page-title
    composition moves into @agenta/shared; the AI SDK's optional zod peer stops warning on the
    mobile build.
    Not run in a browser — static gates only (pnpm lint-fix 24/24, tsc --noEmit clean
    for @agenta/shared, ui, entities, entity-ui, settings-ui, oss, ee, mobile).

Stacked on pkg/settings-ee-pages; review only this lane's diff.

@ardaerzin

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@vercel

vercel Bot commented Aug 10, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
agenta-documentation Error Error Aug 11, 2026 6:01am

Request Review

@coderabbitai

coderabbitai Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: f7a7db51-47c7-4224-afc2-2c84a1fb0417

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Added mobile settings views for LLM providers, secrets, and webhooks.
    • Added forms for managing provider keys, custom endpoints, secrets, and webhook subscriptions.
    • Added reusable confirmation dialogs with validation, loading, and error feedback.
    • Workflow references are now available in agent tools and configuration views.
  • Improvements

    • Settings dialogs adapt between bottom sheets on mobile and side drawers on larger screens.
    • Page titles are more consistent, readable, and safely truncated.
    • Data tables have improved responsive layouts for search, actions, and empty states.

Walkthrough

The PR centralizes workflow-reference and page-title utilities, adds mobile settings tabs and responsive sheets, migrates atomFamily imports to jotai-family, and updates mobile navigation, build configuration, and responsive data-table layouts.

Changes

Shared workflow and title infrastructure

Layer / File(s) Summary
Workflow-reference bridge
web/packages/agenta-entity-ui/..., web/oss/..., web/mobile/src/features/chat/...
Workflow-reference resolution moves to a shared bridge and is wired into OSS and mobile DrillIn providers.
Page-title formatting
web/packages/agenta-shared/src/utils/..., web/mobile/src/components/PageTitle.tsx, web/oss/src/components/PageTitle/..., web/mobile/src/features/...
Shared normalization, truncation, and formatting utilities replace local title-part handling across mobile and OSS screens.

Mobile settings and responsive UI

Layer / File(s) Summary
Settings tabs and forms
web/mobile/src/features/settings/...
Mobile provider, secret, and webhook tabs add create, edit, delete, confirmation, error, pending, and one-time secret flows.
Responsive sheets
web/mobile/src/components/ui/sheet.tsx, web/mobile/src/features/settings/...
SheetContent adds a responsive bottom-sheet-to-drawer mode, and settings sheets use it.
Settings access and navigation
web/mobile/src/features/settings/SettingsScreen.tsx, web/mobile/src/features/settings/settingsTabs.ts
Settings routing uses the new tab wrappers, writable tools and triggers, shared access helpers, and a labeled narrow-layout header.

State and UI integration

Layer / File(s) Summary
Jotai dependency migration
web/mobile/package.json, web/packages/*/package.json, web/packages/*/src/state/...
Packages declare jotai-family, and atom-family imports use it without changing atom behavior.
Build and responsive components
web/mobile/next.config.ts, web/mobile/src/features/nav/..., web/packages/agenta-ui/..., web/packages/agenta-entity-ui/...
Production webpack ignores optional dependencies, navigation uses unwrap, and data-table and operation-section layouts use responsive structure and shared color variables.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 60.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the main frontend changes: mobile DataTable behavior, writable mobile Tools and Triggers, and the shared drill-in bridge.
Description check ✅ Passed The description directly explains the changes, affected areas, validation performed, and known browser-testing limitation.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 2
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch pkg/ui-data-table-responsive

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai

coderabbitai Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Rate Limit Exceeded

@ardaerzin have exceeded the limit for the number of chat messages per hour. Please wait 52 minutes and 3 seconds before sending another message.

@ardaerzin

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 12

🧹 Nitpick comments (6)
web/packages/agenta-entity-ui/src/DrillInView/bridges/useWorkflowReferenceBridge.ts (2)

537-590: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Deduplicate the three retrieveWorkflowRevision calls per workflow.

resolveInputSchema, resolveOutputSchema, and resolveConfigPayload each fetch the same revision by slug. When the reference drawer opens, this produces three identical requests, and the three results can disagree if a commit lands between them.

Cache the revision per projectId+slug, for example through a shared promise map or a TanStack Query fetchQuery, and have all three resolvers read it.


46-46: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Import atomFamily from jotai-family.

This file still imports it from jotai/utils, while the package uses jotai-family elsewhere. The package already declares jotai-family as a peer dependency.

web/mobile/src/components/PageTitle.tsx (1)

4-8: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Shorten the in-code documentation.

This four-line comment duplicates the formatter contract. Replace it with one short line, or move the full behavior description to external documentation.

As per coding guidelines, keep in-code comments to at most one short line unless they document a genuinely surprising constraint.

Source: Coding guidelines

web/mobile/src/features/settings/LlmProvidersTab.tsx (1)

17-19: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low value

Both provider tables share one deleting and error state.

deleteDialog reads the same error for the standard table and the custom table. If a delete fails in one table, and the user then opens the delete confirm in the other table, the stale message appears. The table controls open directly, so onClose does not run first.

Key the state by provider id, or reset error when selectedProvider changes.

Also applies to: 21-62

web/mobile/src/features/settings/SecretFormSheet.tsx (1)

191-202: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Extract the textarea styling into a shared component.

The class list on Line 201 restates the Input component's border, focus-ring, and dark-mode rules. A copy drifts from Input the next time the input styling changes. Add a Textarea component next to @/components/ui/input and use it here.

web/mobile/src/features/settings/useConfirmSheet.tsx (1)

5-9: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Let the caller set the confirm label and the destructive style.

ConfirmSheet defaults destructive to true, and this hook always passes confirmLabel="Confirm". Every confirmation from the shared sections therefore shows a red generic button, including a non-destructive one. Add optional confirmLabel and destructive fields to ConfirmRequest and forward them.

Also applies to: 32-54


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: 16641e06-c39c-4abc-b4b5-39b81fd6cd91

📥 Commits

Reviewing files that changed from the base of the PR and between 9ceda16 and 4fd0ddd.

⛔ Files ignored due to path filters (1)
  • web/pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (87)
  • web/mobile/next.config.ts
  • web/mobile/package.json
  • web/mobile/src/components/PageTitle.tsx
  • web/mobile/src/components/ui/sheet.tsx
  • web/mobile/src/features/agents/AgentListScreen.tsx
  • web/mobile/src/features/agents/AgentOverviewScreen.tsx
  • web/mobile/src/features/agents/AgentTemplateDetailScreen.tsx
  • web/mobile/src/features/agents/AgentTemplatesScreen.tsx
  • web/mobile/src/features/chat/ConfigPane.tsx
  • web/mobile/src/features/chat/DrillInBridgeProvider.tsx
  • web/mobile/src/features/chat/SessionTabs.tsx
  • web/mobile/src/features/chat/selectedRevision.ts
  • web/mobile/src/features/context/ContextResolver.tsx
  • web/mobile/src/features/home/HomeScreen.tsx
  • web/mobile/src/features/nav/useMobileNavItems.tsx
  • web/mobile/src/features/sessions/SessionListScreen.tsx
  • web/mobile/src/features/settings/AccountTab.tsx
  • web/mobile/src/features/settings/CancelSubscriptionSheet.tsx
  • web/mobile/src/features/settings/ConfirmSheet.tsx
  • web/mobile/src/features/settings/CustomEndpointSheet.tsx
  • web/mobile/src/features/settings/Field.tsx
  • web/mobile/src/features/settings/LlmProvidersTab.tsx
  • web/mobile/src/features/settings/MembersTab.tsx
  • web/mobile/src/features/settings/PlanChooserSheet.tsx
  • web/mobile/src/features/settings/ProjectsTab.tsx
  • web/mobile/src/features/settings/ProviderKeySheet.tsx
  • web/mobile/src/features/settings/SecretFormSheet.tsx
  • web/mobile/src/features/settings/SecretsTab.tsx
  • web/mobile/src/features/settings/SettingsScreen.tsx
  • web/mobile/src/features/settings/WebhookFormSheet.tsx
  • web/mobile/src/features/settings/WebhooksTab.tsx
  • web/mobile/src/features/settings/settingsTabs.ts
  • web/mobile/src/features/settings/useConfirmSheet.tsx
  • web/oss/src/components/AgentChatSlice/assets/pageTitle.ts
  • web/oss/src/components/DrillInView/OSSdrillInUIProvider.tsx
  • web/oss/src/components/PageTitle/index.tsx
  • web/packages/agenta-chat/package.json
  • web/packages/agenta-chat/src/state/expandState.ts
  • web/packages/agenta-chat/src/state/sessionMessages.ts
  • web/packages/agenta-entities/src/drive/agentDrive.ts
  • web/packages/agenta-entities/src/drive/configDrive.ts
  • web/packages/agenta-entities/src/drive/driveMedia.ts
  • web/packages/agenta-entities/src/drive/useDriveSelection.ts
  • web/packages/agenta-entities/src/event/state/selectors.ts
  • web/packages/agenta-entities/src/gatewayTool/hooks/useToolActionDetail.ts
  • web/packages/agenta-entities/src/gatewayTool/hooks/useToolCatalogActions.ts
  • web/packages/agenta-entities/src/gatewayTool/hooks/useToolConnectionQuery.ts
  • web/packages/agenta-entities/src/gatewayTool/hooks/useToolIntegrationConnections.ts
  • web/packages/agenta-entities/src/gatewayTool/hooks/useToolIntegrationDetail.ts
  • web/packages/agenta-entities/src/gatewayTool/prompt/atoms.ts
  • web/packages/agenta-entities/src/gatewayTrigger/hooks/useTriggerCatalogEvents.ts
  • web/packages/agenta-entities/src/gatewayTrigger/hooks/useTriggerConnections.ts
  • web/packages/agenta-entities/src/gatewayTrigger/hooks/useTriggerDeliveries.ts
  • web/packages/agenta-entities/src/gatewayTrigger/hooks/useTriggerEvent.ts
  • web/packages/agenta-entities/src/gatewayTrigger/hooks/useTriggerSchedule.ts
  • web/packages/agenta-entities/src/gatewayTrigger/hooks/useTriggerSubscription.ts
  • web/packages/agenta-entities/src/gatewayTrigger/hooks/useTriggerSubscriptions.ts
  • web/packages/agenta-entities/src/session/state/fileActivity.ts
  • web/packages/agenta-entities/src/session/state/mounts.ts
  • web/packages/agenta-entities/src/session/state/records.ts
  • web/packages/agenta-entities/src/webhook/atoms.ts
  • web/packages/agenta-entities/src/workflow/relations.ts
  • web/packages/agenta-entities/src/workflow/state/inspectMeta.ts
  • web/packages/agenta-entity-ui/src/DrillInView/SchemaControls/AgentOperationsSections.tsx
  • web/packages/agenta-entity-ui/src/DrillInView/bridges/useWorkflowReferenceBridge.ts
  • web/packages/agenta-entity-ui/src/DrillInView/index.ts
  • web/packages/agenta-entity-ui/src/agent/state.ts
  • web/packages/agenta-entity-ui/src/drive/SessionFilesDrawer.tsx
  • web/packages/agenta-entity-ui/src/drive/quickLook.tsx
  • web/packages/agenta-entity-ui/src/secretProvider/CustomProviderForm.tsx
  • web/packages/agenta-navigation/package.json
  • web/packages/agenta-navigation/src/state.ts
  • web/packages/agenta-observability/package.json
  • web/packages/agenta-observability/src/state/index.ts
  • web/packages/agenta-playground-ui/src/components/WorkflowRevisionDrawer/DrawerContent.tsx
  • web/packages/agenta-playground/src/state/execution/channelMode.ts
  • web/packages/agenta-sessions/package.json
  • web/packages/agenta-sessions/src/state/tabOrder.ts
  • web/packages/agenta-settings-ui/package.json
  • web/packages/agenta-settings-ui/src/tools/hooks/useIntegrationDetail.ts
  • web/packages/agenta-shared/package.json
  • web/packages/agenta-shared/src/state/featureFlags.ts
  • web/packages/agenta-shared/src/state/simulatedAgentRun.ts
  • web/packages/agenta-shared/src/utils/index.ts
  • web/packages/agenta-shared/src/utils/pageTitle.ts
  • web/packages/agenta-shared/tests/unit/pageTitle.test.ts
  • web/packages/agenta-ui/src/components/ui/data-table.tsx

Comment on lines +34 to +39
useEffect(() => {
if (!open) return
setKey(provider?.key ?? "")
setError(null)
setSaving(false)
}, [open, provider])

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Both edit sheets seed a form field from a stored credential. The shared root cause is the reset effect: each sheet loads the persisted secret into a plain-text control instead of starting empty and treating an empty field as "keep the stored value". That either exposes a credential on screen or writes a masked value back to the vault. WebhookFormSheet.tsx Lines 90-93 already applies the empty-means-unchanged rule, so adopt it in both sheets.

  • web/mobile/src/features/settings/ProviderKeySheet.tsx#L34-L39: replace setKey(provider?.key ?? "") with setKey(""), and skip the key in handleModifyVaultSecret when the field is empty.
  • web/mobile/src/features/settings/SecretFormSheet.tsx#L70-L85: stop loading secret.content into value when secret exists, relax canSubmit so an edit does not require the value, and omit content from the payload when the field is empty.
📍 Affects 2 files
  • web/mobile/src/features/settings/ProviderKeySheet.tsx#L34-L39 (this comment)
  • web/mobile/src/features/settings/SecretFormSheet.tsx#L70-L85

Comment on lines +132 to +134
// Destructive actions in the shared tool/trigger sections ask for confirmation through an
// imperative callback (the desktop hands them antd's AlertPopup); this is the sheet version.
const {confirm, sheet: confirmSheet} = useConfirmSheet()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Reset pending confirmations when tab changes.

If tab changes while a request is pending, TabBody remains mounted and useConfirmSheet keeps the request. The sheet is rendered in the tools and triggers cases, so returning can reopen the old destructive action. Reset the request on tab changes, or remount TabBody per tab.

Also applies to: 189-202

Comment on lines 189 to +194
case "tools":
return <GatewayToolsSection readOnly />
return (
<>
<GatewayToolsSection confirm={confirm} />
{confirmSheet}
</>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n -C 8 \
  '\bGatewayToolsSection\b|\buseMobileSettingsAccess\b|tools.*(access|gate)|((access|gate).*tools)' \
  web/mobile web/packages

Repository: Agenta-AI/agenta

Length of output: 50372


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- SettingsScreen ---'
sed -n '1,230p' web/mobile/src/features/settings/SettingsScreen.tsx

printf '%s\n' '--- settingsTabs ---'
sed -n '1,260p' web/mobile/src/features/settings/settingsTabs.tsx

printf '%s\n' '--- GatewayToolsSection ---'
sed -n '1,300p' web/packages/agenta-settings-ui/src/tools/GatewayToolsSection.tsx

printf '%s\n' '--- access-related definitions and uses ---'
rg -n -C 5 'useMobileSettingsAccess|canShowTools|readOnly|GatewayToolsSection' \
  web/mobile/src/features/settings web/packages/agenta-settings-ui/src/tools

Repository: Agenta-AI/agenta

Length of output: 9065


🏁 Script executed:

#!/bin/bash
set -euo pipefail

settings_tabs="$(fd -i '^settingsTabs\.' web/mobile/src/features/settings)"
printf '%s\n' "--- settingsTabs candidates: ${settings_tabs} ---"
for file in $settings_tabs; do
  sed -n '1,280p' "$file"
done

printf '%s\n' '--- remaining SettingsScreen ---'
sed -n '230,520p' web/mobile/src/features/settings/SettingsScreen.tsx

printf '%s\n' '--- GatewayToolsSection ---'
sed -n '1,360p' web/packages/agenta-settings-ui/src/tools/GatewayToolsSection.tsx

printf '%s\n' '--- direct access and readOnly references ---'
rg -n -C 6 'useMobileSettingsAccess|canShowTools|readOnly|GatewayToolsSection' \
  web/mobile/src/features/settings web/packages/agenta-settings-ui/src/tools

Repository: Agenta-AI/agenta

Length of output: 45583


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
from pathlib import Path

screen = Path("web/mobile/src/features/settings/SettingsScreen.tsx").read_text()
tabs = Path("web/mobile/src/features/settings/settingsTabs.ts").read_text()
section = Path("web/packages/agenta-settings-ui/src/tools/GatewayToolsSection.tsx").read_text()

checks = {
    "tools is an accepted direct-query tab": '"tools"' in tabs and "AVAILABLE_SETTINGS_TABS.includes(requested as SettingsTabKey)" in tabs,
    "direct-query resolution does not apply canShowTools": 'return AVAILABLE_SETTINGS_TABS.includes(requested as SettingsTabKey)' in tabs,
    "tools body renders GatewayToolsSection": '<GatewayToolsSection confirm={confirm} />' in screen,
    "tools body does not pass readOnly": '<GatewayToolsSection confirm={confirm} readOnly=' not in screen,
    "GatewayToolsSection defaults readOnly to false": 'function GatewayToolsSection({confirm, readOnly}: GatewayToolsSectionProps)' in section,
    "connect action is hidden only by readOnly": 'hidden: readOnly' in section and '{readOnly ? null : <CatalogDrawer' in section,
}

for name, result in checks.items():
    print(f"{'PASS' if result else 'FAIL'}: {name}")

if not all(checks.values()):
    raise SystemExit(1)
PY

printf '%s\n' '--- shared feature helper and desktop access ---'
rg -n -C 8 'function isToolsEnabled|const isToolsEnabled|isToolsEnabled|useSettingsAccess|canShowTools' \
  web/packages web/ee web/oss 2>/dev/null || true

Repository: Agenta-AI/agenta

Length of output: 26653


Enforce the tools access gate at the render boundary. Direct ?tab=tools navigation bypasses canShowTools, and GatewayToolsSection renders writable controls because readOnly is unset. Do not render this section when access.canShowTools is false.

setSaving(false)
setName(editing?.name ?? "")
setUrl(editing?.data?.url ?? "")
setEventType(editing?.data?.event_types?.[0] ?? EVENT_OPTIONS[0].value)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Editing a subscription discards every event type except the first.

Line 63 loads only event_types[0]. Line 78 writes event_types: [eventType]. If a subscription was created elsewhere with several event types, a save from this sheet drops the rest without warning. Today EVENT_OPTIONS holds one entry, so the sheet cannot restore them.

Preserve the unedited types, or block editing when the stored list holds more than one type.

🛡️ Proposed fix: keep the types the sheet cannot represent
     const [eventType, setEventType] = useState<WebhookEventType>(EVENT_OPTIONS[0].value)
+    const [extraEventTypes, setExtraEventTypes] = useState<WebhookEventType[]>([])
@@
         setEventType(editing?.data?.event_types?.[0] ?? EVENT_OPTIONS[0].value)
+        setExtraEventTypes(editing?.data?.event_types?.slice(1) ?? [])
@@
         const data = {
             url: url.trim(),
-            event_types: [eventType],
+            event_types: [eventType, ...extraEventTypes],
             auth_mode: authMode,
         }

Also applies to: 76-80

Comment on lines +107 to +109
// A signed subscription mints a secret the backend returns exactly once.
const secret = created.subscription?.secret || created.subscription?.secret_id
if (authMode === "signature" && secret) setCreatedSecret(secret)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check the create-webhook response shape and how the desktop reads the one-time secret.
rg -n --glob '!**/node_modules/**' -C5 -e 'secret_id' -e 'createdWebhookSecretAtom' web/packages web/oss
fd -t f 'atoms.ts' web/packages/agenta-entities/src/webhook --exec cat -n {}

Repository: Agenta-AI/agenta

Length of output: 40710


🏁 Script executed:

#!/bin/bash
set -e
printf '%s\n' '--- mobile form ---'
sed -n '70,135p' web/mobile/src/features/settings/WebhookFormSheet.tsx
printf '%s\n' '--- mobile consumers ---'
rg -n -C6 'createdWebhookSecretAtom|createdWebhookSecret|secret_id|secret' web/mobile/src/features/settings web/mobile/src
printf '%s\n' '--- webhook entity/API definitions ---'
sed -n '1,120p' web/packages/agenta-entities/src/webhook/types.ts
rg -n -C8 'createWebhookSubscription|WebhookSubscription' web/packages/agenta-entities/src/webhook web/packages/agenta-api-client/src/generated/api/resources/webhooks web/packages/agenta-api-client/src/generated/api/types
printf '%s\n' '--- backend webhook implementation references ---'
rg -n -C6 'secret_id|secret.*signature|signature.*secret|WebhookSubscription' backend web --glob '!**/node_modules/**' --glob '!web/packages/agenta-api-client/src/generated/**' | head -n 500

Repository: Agenta-AI/agenta

Length of output: 50373


🏁 Script executed:

#!/bin/bash
set -e
python3 - <<'PY'
from pathlib import Path
p = Path("web/mobile/src/features/settings/WebhookFormSheet.tsx")
text = p.read_text()
needle = "created.subscription?.secret || created.subscription?.secret_id"
print("fallback_present:", needle in text)
print("secret_only_present:", "const secret = created.subscription?.secret" in text)
PY

Repository: Agenta-AI/agenta

Length of output: 203


🏁 Script executed:

#!/bin/bash
set -e
printf '%s\n' '--- webhook-related files ---'
git ls-files | rg -i 'webhook|subscription' | head -n 200
printf '%s\n' '--- generated response and request types ---'
rg -l 'interface WebhookSubscription|type WebhookSubscription|secret_id' web/packages/agenta-api-client/src/generated/api | head -n 50
for f in $(rg -l 'interface WebhookSubscription|type WebhookSubscription' web/packages/agenta-api-client/src/generated/api | head -n 10); do
  echo "--- $f"
  cat -n "$f"
done
printf '%s\n' '--- webhook API wrappers ---'
rg -n -C8 'createWebhookSubscription|createWebhook' web/packages/agenta-entities/src/webhook web/packages/agenta-entities/src --glob '*.ts'
printf '%s\n' '--- mobile secret reveal block ---'
sed -n '25,120p' web/mobile/src/features/settings/WebhooksTab.tsx

Repository: Agenta-AI/agenta

Length of output: 36842


🏁 Script executed:

#!/bin/bash
set -e
python3 - <<'PY'
from pathlib import Path
form = Path("web/mobile/src/features/settings/WebhookFormSheet.tsx").read_text()
tab = Path("web/mobile/src/features/settings/WebhooksTab.tsx").read_text()
checks = {
    "fallback_uses_secret_id": "created.subscription?.secret || created.subscription?.secret_id" in form,
    "only_secret_is_rendered": "{createdSecret}" in tab,
    "success_runs_without_secret_error": "onSuccess()" in form and "setError(" in form,
    "secret_modal_requires_atom": "createdSecret" in tab and "Save your webhook secret" in tab,
}
for name, value in checks.items():
    print(f"{name}: {value}")
PY

Repository: Agenta-AI/agenta

Length of output: 287


🏁 Script executed:

#!/bin/bash
set -e
printf '%s\n' '--- exact generated webhook model ---'
f=$(rg -l 'interface WebhookSubscription' web/packages/agenta-api-client/src/generated/api | head -n 1)
test -n "$f" && { echo "$f"; cat -n "$f"; }
printf '%s\n' '--- exact webhook entity types and API ---'
cat -n web/packages/agenta-entities/src/webhook/types.ts | sed -n '1,90p'
cat -n web/packages/agenta-entities/src/webhook/api.ts 2>/dev/null || true
cat -n web/packages/agenta-entities/src/webhook/api/api.ts 2>/dev/null || true
printf '%s\n' '--- form setup and completion flow ---'
sed -n '1,75p' web/mobile/src/features/settings/WebhookFormSheet.tsx
sed -n '90,125p' web/mobile/src/features/settings/WebhooksTab.tsx
printf '%s\n' '--- backend candidates containing webhook route/schema ---'
rg -l -i 'webhook' --glob '!web/packages/agenta-api-client/src/generated/**' --glob '!**/node_modules/**' | head -n 100

Repository: Agenta-AI/agenta

Length of output: 12652


🏁 Script executed:

#!/bin/bash
set -e
printf '%s\n' '--- backend webhook models and secret handling ---'
cat -n api/oss/src/apis/fastapi/webhooks/models.py | sed -n '1,240p'
cat -n api/oss/src/core/webhooks/types.py | sed -n '1,260p'
rg -n -C10 'secret_id|secret\b|auth_mode' api/oss/src/apis/fastapi/webhooks api/oss/src/core/webhooks api/oss/tests/pytest/acceptance/webhooks api/oss/tests/pytest/unit/webhooks
printf '%s\n' '--- API docs for create response ---'
rg -n -C8 'secret_id|secret' docs/docs/reference/api/create-webhook-subscription* clients/python/agenta_client/types/webhook_subscription*.py

Repository: Agenta-AI/agenta

Length of output: 50373


🏁 Script executed:

#!/bin/bash
set -e
python3 - <<'PY'
from pathlib import Path
paths = [
    Path("api/oss/src/apis/fastapi/webhooks/models.py"),
    Path("api/oss/src/core/webhooks/types.py"),
    Path("web/mobile/src/features/settings/WebhookFormSheet.tsx"),
]
for path in paths:
    text = path.read_text()
    print(path)
    for term in ("secret_id", "secret", "auth_mode", "onSuccess()", "close()"):
        print(f"  {term}: {text.count(term)}")
PY

Repository: Agenta-AI/agenta

Length of output: 504


Use only secret as the webhook secret.

secret_id identifies the stored secret. It is not the signing secret. If secret is absent, show an error and do not continue to onSuccess() or close(). Otherwise, WebhooksTab.tsx displays an unusable identifier for signature verification.

Comment on lines +94 to +102
<Button
onClick={async () => {
if (!createdSecret) return
await navigator.clipboard?.writeText(createdSecret)
setCopied(true)
}}
>
{copied ? "Copied" : "Copy secret"}
</Button>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

The copy button reports success even when nothing was copied.

Two problems exist on Line 97:

  • If navigator.clipboard is undefined, for example in a non-secure context, the optional chain returns undefined. await undefined resolves, and Line 98 sets the label to "Copied".
  • If writeText rejects, for example because the document lost focus or permission was denied, the rejection is unhandled and the user sees no error.

The secret appears exactly once, so a false "Copied" makes the user lose it. Handle both paths and keep the sheet open on failure.

🐛 Proposed fix
+    const [copyError, setCopyError] = useState<string | null>(null)
@@
                             <Button
                                 onClick={async () => {
                                     if (!createdSecret) return
-                                    await navigator.clipboard?.writeText(createdSecret)
-                                    setCopied(true)
+                                    try {
+                                        if (!navigator.clipboard) {
+                                            throw new Error("Clipboard access is unavailable.")
+                                        }
+                                        await navigator.clipboard.writeText(createdSecret)
+                                        setCopyError(null)
+                                        setCopied(true)
+                                    } catch {
+                                        setCopyError("Could not copy. Select the secret and copy it manually.")
+                                    }
                                 }}
                             >
                                 {copied ? "Copied" : "Copy secret"}
                             </Button>

Render copyError above the footer, in the same style the other sheets use for errors.

"@ai-sdk/react": ">=3.0.0-beta.0",
"ai": ">=6.0.0-beta.0",
"jotai": ">=2.0.0",
"jotai-family": ">=0.1.0",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

python - <<'PY'
import json
from pathlib import Path

for path in Path(".").rglob("package.json"):
    if "node_modules" in path.parts or ".git" in path.parts:
        continue

    data = json.loads(path.read_text())
    deps = {}
    for section in ("dependencies", "devDependencies", "peerDependencies", "optionalDependencies"):
        deps.update(data.get(section, {}))

    if "`@agenta/chat`" in deps or "`@agenta/sessions`" in deps:
        if "jotai-family" not in deps:
            print(f"Missing jotai-family declaration: {path}")
PY

Repository: Agenta-AI/agenta

Length of output: 308


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

python3 - <<'PY'
import json
from pathlib import Path

for path in sorted(Path("web").rglob("package.json")):
    if "node_modules" in path.parts:
        continue
    data = json.loads(path.read_text())
    name = data.get("name")
    refs = {}
    for section in ("dependencies", "devDependencies", "peerDependencies", "optionalDependencies"):
        for dep, version in data.get(section, {}).items():
            if dep in ("`@agenta/chat`", "`@agenta/sessions`", "jotai-family"):
                refs.setdefault(section, {})[dep] = version
    if refs or name in ("`@agenta/chat`", "`@agenta/sessions`"):
        print(f"\n{path}")
        print(json.dumps({"name": name, **refs}, indent=2, sort_keys=True))

print("\nSource imports of jotai-family:")
for path in sorted(Path("web").rglob("*")):
    if not path.is_file() or "node_modules" in path.parts:
        continue
    try:
        text = path.read_text()
    except (UnicodeDecodeError, OSError):
        continue
    if "jotai-family" in text:
        print(path)
PY

Repository: Agenta-AI/agenta

Length of output: 10799


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

printf '%s\n' 'Consumer package manifests:'
sed -n '1,90p' web/packages/agenta-home-ui/package.json
sed -n '1,70p' web/packages/agenta-sessions-ui/package.json

printf '%s\n' 'Consumer source imports:'
rg -n --glob '!node_modules/**' --glob '!package.json' \
  '(`@agenta/`(chat|sessions)|agenta-chat|agenta-sessions)' \
  web/packages/agenta-home-ui web/packages/agenta-sessions-ui || true

printf '%s\n' 'Provider source imports:'
rg -n --glob '!node_modules/**' 'from ["'\'']jotai-family["'\'']|require\(["'\'']jotai-family["'\'']\)' \
  web/packages/agenta-chat web/packages/agenta-sessions

Repository: Agenta-AI/agenta

Length of output: 6083


Declare jotai-family in both consumer packages.

@agenta/home-ui and @agenta/sessions-ui consume packages that require jotai-family as a peer dependency. Add jotai-family to both package manifests.

📍 Affects 2 files
  • web/packages/agenta-chat/package.json#L43-L43 (this comment)
  • web/packages/agenta-sessions/package.json#L30-L30

sticky ? "sticky top-0 z-[10]" : ""
} w-full border-b border-colorBorderSecondary py-2 px-4 bg-[var(--ag-c-FFFFFF)] bg-[image:linear-gradient(var(--ant-color-fill-tertiary),var(--ant-color-fill-tertiary))]`
const titleClass = "text-[13px] font-semibold text-[var(--ant-color-text)]"
} w-full border-b border-colorBorderSecondary py-2 px-4 bg-[var(--ag-c-FFFFFF)] bg-[image:linear-gradient(var(--ag-colorFillTertiary),var(--ag-colorFillTertiary))]`

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Replace the legacy color literal.

Line 30 uses --ag-c-FFFFFF, which the theme rules prohibit. Use a semantic background token such as bg-colorBgContainer for the fallback color.

Proposed fix
-    } w-full border-b border-colorBorderSecondary py-2 px-4 bg-[var(--ag-c-FFFFFF)] bg-[image:linear-gradient(var(--ag-colorFillTertiary),var(--ag-colorFillTertiary))]`
+    } w-full border-b border-colorBorderSecondary py-2 px-4 bg-colorBgContainer bg-[image:linear-gradient(var(--ag-colorFillTertiary),var(--ag-colorFillTertiary))]`
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
} w-full border-b border-colorBorderSecondary py-2 px-4 bg-[var(--ag-c-FFFFFF)] bg-[image:linear-gradient(var(--ag-colorFillTertiary),var(--ag-colorFillTertiary))]`
} w-full border-b border-colorBorderSecondary py-2 px-4 bg-colorBgContainer bg-[image:linear-gradient(var(--ag-colorFillTertiary),var(--ag-colorFillTertiary))]`

Source: Coding guidelines

"@tanstack/react-query": ">=5.0.0",
"axios": ">=1.13.5 <2.0.0",
"jotai": ">=2.0.0",
"jotai-family": ">=0.1.0",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

python - <<'PY'
import json
from pathlib import Path

peers = json.loads(
    Path("web/packages/agenta-shared/package.json").read_text()
)["peerDependencies"]

print("shared jotai peer:", peers["jotai"])
print("shared jotai-family peer:", peers["jotai-family"])
PY

curl -fsSL https://registry.npmjs.org/jotai-family/1.0.1 \
  | python -c 'import json, sys; print(json.load(sys.stdin).get("peerDependencies", {}))'

rg -n -A5 -B2 'jotai-family@1\.0\.1|jotai:.*2\.9\.0' web/pnpm-lock.yaml

Repository: Agenta-AI/agenta

Length of output: 1928


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

python3 - <<'PY'
import json
import subprocess
from pathlib import Path

pkg = json.loads(Path("web/packages/agenta-shared/package.json").read_text())
print("package peerDependencies:", json.dumps(pkg.get("peerDependencies", {}), sort_keys=True))
print("package dependencies:", json.dumps(pkg.get("dependencies", {}), sort_keys=True))

lock = Path("web/pnpm-lock.yaml").read_text()
for needle in ("jotai-family@1.0.1:", "jotai-family@0.", "jotai-family@1.", "jotai-family:"):
    print(f"\nLOCK MATCHES: {needle}")
    lines = lock.splitlines()
    for i, line in enumerate(lines):
        if needle in line:
            print("\n".join(f"{j+1}: {lines[j]}" for j in range(max(0, i-2), min(len(lines), i+8))))

print("\nJOTAI-FAMILY USAGES")
subprocess.run(
    ["rg", "-n", "--glob", "!pnpm-lock.yaml", r"jotai-family|atomFamily"],
    check=False,
)
PY

curl -fsSL https://registry.npmjs.org/jotai-family \
  | python3 - <<'PY'
import json, sys
data = json.load(sys.stdin)
for version in data.get("versions", {}):
    if version.startswith(("0.", "1.")):
        manifest = data["versions"][version]
        print(version, json.dumps(manifest.get("peerDependencies", {}), sort_keys=True))
PY

Repository: Agenta-AI/agenta

Length of output: 11257


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

echo "JOTAI-FAMILY USAGES"
rg -n --glob '!pnpm-lock.yaml' 'jotai-family|atomFamily' web/packages/agenta-shared web/packages 2>/dev/null || true

echo
echo "JOTAI-FAMILY REGISTRY VERSIONS AND PEERS"
curl -fsSL https://registry.npmjs.org/jotai-family \
  | python3 -c '
import json, sys
data = json.load(sys.stdin)
for version, manifest in data.get("versions", {}).items():
    if version.startswith(("0.", "1.")):
        print(version, json.dumps(manifest.get("peerDependencies", {}), sort_keys=True))
'

echo
echo "JOTAI-FAMILY VERSION METADATA"
for version in 0.1.0 0.2.0 0.2.6 1.0.0 1.0.1; do
  printf "%s: " "$version"
  curl -fsSL "https://registry.npmjs.org/jotai-family/$version" \
    | python3 -c 'import json, sys; d=json.load(sys.stdin); print(json.dumps({"version": d.get("version"), "peerDependencies": d.get("peerDependencies", {}), "engines": d.get("engines", {})}, sort_keys=True))'
done

Repository: Agenta-AI/agenta

Length of output: 50372


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

curl -fsSL https://registry.npmjs.org/jotai-family \
  | python3 -c '
import json, sys
data = json.load(sys.stdin)
for version, manifest in data.get("versions", {}).items():
    if version.startswith(("0.", "1.")):
        print(version, json.dumps(manifest.get("peerDependencies", {}), sort_keys=True))
'

echo
for version in 0.1.0 0.2.0 0.2.6 1.0.0 1.0.1; do
  printf "%s: " "$version"
  curl -fsSL "https://registry.npmjs.org/jotai-family/$version" \
    | python3 -c '
import json, sys
d = json.load(sys.stdin)
print(json.dumps({
    "version": d.get("version"),
    "peerDependencies": d.get("peerDependencies", {}),
    "engines": d.get("engines", {})
}, sort_keys=True))
'
done

Repository: Agenta-AI/agenta

Length of output: 961


Raise the Jotai peer floor to >=2.9.0.

jotai-family requires jotai >=2.9.0. The current jotai >=2.0.0 range permits unsupported Jotai versions. Keep jotai-family >=0.1.0; published versions before 1.0.0 do not satisfy this range.

Comment on lines +145 to +146
// The empty state carries its own call to action, and on a phone the two sit far enough
// apart to read as different controls — so show only that one.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Reduce this source comment to one short line.

The condition already shows the mobile behavior. Keep only a short statement if the comment is needed.

As per coding guidelines, keep in-code comments to at most one short line unless they document a genuinely surprising constraint.

Source: Coding guidelines

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.

Actionable comments posted: 12

🧹 Nitpick comments (6)
web/packages/agenta-entity-ui/src/DrillInView/bridges/useWorkflowReferenceBridge.ts (2)

537-590: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Deduplicate the three retrieveWorkflowRevision calls per workflow.

resolveInputSchema, resolveOutputSchema, and resolveConfigPayload each fetch the same revision by slug. When the reference drawer opens, this produces three identical requests, and the three results can disagree if a commit lands between them.

Cache the revision per projectId+slug, for example through a shared promise map or a TanStack Query fetchQuery, and have all three resolvers read it.


46-46: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Import atomFamily from jotai-family.

This file still imports it from jotai/utils, while the package uses jotai-family elsewhere. The package already declares jotai-family as a peer dependency.

web/mobile/src/components/PageTitle.tsx (1)

4-8: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Shorten the in-code documentation.

This four-line comment duplicates the formatter contract. Replace it with one short line, or move the full behavior description to external documentation.

As per coding guidelines, keep in-code comments to at most one short line unless they document a genuinely surprising constraint.

Source: Coding guidelines

web/mobile/src/features/settings/LlmProvidersTab.tsx (1)

17-19: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low value

Both provider tables share one deleting and error state.

deleteDialog reads the same error for the standard table and the custom table. If a delete fails in one table, and the user then opens the delete confirm in the other table, the stale message appears. The table controls open directly, so onClose does not run first.

Key the state by provider id, or reset error when selectedProvider changes.

Also applies to: 21-62

web/mobile/src/features/settings/SecretFormSheet.tsx (1)

191-202: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Extract the textarea styling into a shared component.

The class list on Line 201 restates the Input component's border, focus-ring, and dark-mode rules. A copy drifts from Input the next time the input styling changes. Add a Textarea component next to @/components/ui/input and use it here.

web/mobile/src/features/settings/useConfirmSheet.tsx (1)

5-9: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Let the caller set the confirm label and the destructive style.

ConfirmSheet defaults destructive to true, and this hook always passes confirmLabel="Confirm". Every confirmation from the shared sections therefore shows a red generic button, including a non-destructive one. Add optional confirmLabel and destructive fields to ConfirmRequest and forward them.

Also applies to: 32-54


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: 16641e06-c39c-4abc-b4b5-39b81fd6cd91

📥 Commits

Reviewing files that changed from the base of the PR and between 9ceda16 and 4fd0ddd.

⛔ Files ignored due to path filters (1)
  • web/pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (87)
  • web/mobile/next.config.ts
  • web/mobile/package.json
  • web/mobile/src/components/PageTitle.tsx
  • web/mobile/src/components/ui/sheet.tsx
  • web/mobile/src/features/agents/AgentListScreen.tsx
  • web/mobile/src/features/agents/AgentOverviewScreen.tsx
  • web/mobile/src/features/agents/AgentTemplateDetailScreen.tsx
  • web/mobile/src/features/agents/AgentTemplatesScreen.tsx
  • web/mobile/src/features/chat/ConfigPane.tsx
  • web/mobile/src/features/chat/DrillInBridgeProvider.tsx
  • web/mobile/src/features/chat/SessionTabs.tsx
  • web/mobile/src/features/chat/selectedRevision.ts
  • web/mobile/src/features/context/ContextResolver.tsx
  • web/mobile/src/features/home/HomeScreen.tsx
  • web/mobile/src/features/nav/useMobileNavItems.tsx
  • web/mobile/src/features/sessions/SessionListScreen.tsx
  • web/mobile/src/features/settings/AccountTab.tsx
  • web/mobile/src/features/settings/CancelSubscriptionSheet.tsx
  • web/mobile/src/features/settings/ConfirmSheet.tsx
  • web/mobile/src/features/settings/CustomEndpointSheet.tsx
  • web/mobile/src/features/settings/Field.tsx
  • web/mobile/src/features/settings/LlmProvidersTab.tsx
  • web/mobile/src/features/settings/MembersTab.tsx
  • web/mobile/src/features/settings/PlanChooserSheet.tsx
  • web/mobile/src/features/settings/ProjectsTab.tsx
  • web/mobile/src/features/settings/ProviderKeySheet.tsx
  • web/mobile/src/features/settings/SecretFormSheet.tsx
  • web/mobile/src/features/settings/SecretsTab.tsx
  • web/mobile/src/features/settings/SettingsScreen.tsx
  • web/mobile/src/features/settings/WebhookFormSheet.tsx
  • web/mobile/src/features/settings/WebhooksTab.tsx
  • web/mobile/src/features/settings/settingsTabs.ts
  • web/mobile/src/features/settings/useConfirmSheet.tsx
  • web/oss/src/components/AgentChatSlice/assets/pageTitle.ts
  • web/oss/src/components/DrillInView/OSSdrillInUIProvider.tsx
  • web/oss/src/components/PageTitle/index.tsx
  • web/packages/agenta-chat/package.json
  • web/packages/agenta-chat/src/state/expandState.ts
  • web/packages/agenta-chat/src/state/sessionMessages.ts
  • web/packages/agenta-entities/src/drive/agentDrive.ts
  • web/packages/agenta-entities/src/drive/configDrive.ts
  • web/packages/agenta-entities/src/drive/driveMedia.ts
  • web/packages/agenta-entities/src/drive/useDriveSelection.ts
  • web/packages/agenta-entities/src/event/state/selectors.ts
  • web/packages/agenta-entities/src/gatewayTool/hooks/useToolActionDetail.ts
  • web/packages/agenta-entities/src/gatewayTool/hooks/useToolCatalogActions.ts
  • web/packages/agenta-entities/src/gatewayTool/hooks/useToolConnectionQuery.ts
  • web/packages/agenta-entities/src/gatewayTool/hooks/useToolIntegrationConnections.ts
  • web/packages/agenta-entities/src/gatewayTool/hooks/useToolIntegrationDetail.ts
  • web/packages/agenta-entities/src/gatewayTool/prompt/atoms.ts
  • web/packages/agenta-entities/src/gatewayTrigger/hooks/useTriggerCatalogEvents.ts
  • web/packages/agenta-entities/src/gatewayTrigger/hooks/useTriggerConnections.ts
  • web/packages/agenta-entities/src/gatewayTrigger/hooks/useTriggerDeliveries.ts
  • web/packages/agenta-entities/src/gatewayTrigger/hooks/useTriggerEvent.ts
  • web/packages/agenta-entities/src/gatewayTrigger/hooks/useTriggerSchedule.ts
  • web/packages/agenta-entities/src/gatewayTrigger/hooks/useTriggerSubscription.ts
  • web/packages/agenta-entities/src/gatewayTrigger/hooks/useTriggerSubscriptions.ts
  • web/packages/agenta-entities/src/session/state/fileActivity.ts
  • web/packages/agenta-entities/src/session/state/mounts.ts
  • web/packages/agenta-entities/src/session/state/records.ts
  • web/packages/agenta-entities/src/webhook/atoms.ts
  • web/packages/agenta-entities/src/workflow/relations.ts
  • web/packages/agenta-entities/src/workflow/state/inspectMeta.ts
  • web/packages/agenta-entity-ui/src/DrillInView/SchemaControls/AgentOperationsSections.tsx
  • web/packages/agenta-entity-ui/src/DrillInView/bridges/useWorkflowReferenceBridge.ts
  • web/packages/agenta-entity-ui/src/DrillInView/index.ts
  • web/packages/agenta-entity-ui/src/agent/state.ts
  • web/packages/agenta-entity-ui/src/drive/SessionFilesDrawer.tsx
  • web/packages/agenta-entity-ui/src/drive/quickLook.tsx
  • web/packages/agenta-entity-ui/src/secretProvider/CustomProviderForm.tsx
  • web/packages/agenta-navigation/package.json
  • web/packages/agenta-navigation/src/state.ts
  • web/packages/agenta-observability/package.json
  • web/packages/agenta-observability/src/state/index.ts
  • web/packages/agenta-playground-ui/src/components/WorkflowRevisionDrawer/DrawerContent.tsx
  • web/packages/agenta-playground/src/state/execution/channelMode.ts
  • web/packages/agenta-sessions/package.json
  • web/packages/agenta-sessions/src/state/tabOrder.ts
  • web/packages/agenta-settings-ui/package.json
  • web/packages/agenta-settings-ui/src/tools/hooks/useIntegrationDetail.ts
  • web/packages/agenta-shared/package.json
  • web/packages/agenta-shared/src/state/featureFlags.ts
  • web/packages/agenta-shared/src/state/simulatedAgentRun.ts
  • web/packages/agenta-shared/src/utils/index.ts
  • web/packages/agenta-shared/src/utils/pageTitle.ts
  • web/packages/agenta-shared/tests/unit/pageTitle.test.ts
  • web/packages/agenta-ui/src/components/ui/data-table.tsx
🛑 Comments failed to post (1)
web/packages/agenta-shared/src/utils/pageTitle.ts (1)

7-17: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Handle non-positive maxLength before adding the ellipsis.

truncateTitlePart returns "…" when maxLength is 0 or negative and the normalized value is not empty. The result exceeds the requested limit. Return an empty string or reject invalid limits before truncation. Add a regression test for 0.

Proposed fix
     const normalized = normalizeTitlePart(value).replace(/[\uD800-\uDBFF]$/, "")
+    if (maxLength <= 0) return ""
     const characters = Array.from(normalized)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

export const truncateTitlePart = (value: string, maxLength: number): string => {
    // Server-persisted titles may arrive cut on a UTF-16 boundary; a trailing high surrogate
    // has no pair left and renders as a replacement character.
    const normalized = normalizeTitlePart(value).replace(/[\uD800-\uDBFF]$/, "")
    if (maxLength <= 0) return ""
    const characters = Array.from(normalized)
    if (characters.length <= maxLength) return normalized
    return `${characters
        .slice(0, Math.max(0, maxLength - 1))
        .join("")
        .trimEnd()}…`
}

The previous commit claimed this and did not do it. The settings feature had been
refactored in the meantime — the access object moved to settingsTabs.ts — so the
string replace matched nothing and no-op'd silently, and lint/tsc stayed green
because nothing was broken, only unchanged.

canShowTools/canShowTriggers now come from isToolsEnabled(), and isEE from the
shared isEE() rather than a bare `=== "ee"` that misses the cloud tiers.

settingsTabs.ts is the only place mobile builds SettingsAccess, so this is the
whole surface.
readOnly was a guard against the create/edit drawers rendering antd forms with no
ConfigProvider here. That reason is gone — SchemaForm and SubscriptionForm moved
to @rc-component/form, so the drawers carry no antd theming.

Destructive actions in those sections ask for confirmation through an imperative
callback (the desktop passes antd's AlertPopup). useConfirmSheet adapts the
existing ConfirmSheet, which is controlled, to that shape, and reports a failed
action in the sheet rather than swallowing it — this app has no toasts.

Subscriptions and schedules take no confirm: they delete through their own
handlers, not through the shared prompt.

Gates: lint 24/24, tsc 0 on mobile. Not yet exercised in a browser — the drawers
and the confirm path are the things to try.
A section with a title but no search rendered the heading, then the buttons on
their own row below — a band of empty space between them, and the buttons sitting
closer to the table than to the section they act on. The three Triggers sections
all read that way.

The actions now ride the title's row when there is one, and fall back to the end
of the filter row when there is not (Tools, which searches and has no title, is
unchanged). A section with both gets the actions beside the title and the search
on its own row, since the search box is too wide to share.

Aligned to the top of the title block, which is two lines — name over description.

Gates: lint 24/24, tsc 0 across ui, settings-ui, oss and mobile.
Four narrow-viewport fixes, all in the primitive so every settings tab gets them:

The header row stacks below sm instead of wrapping. `ml-auto` meant the buttons
landed hard-right once they wrapped, leaving a gap under the description — they
now span the row.

The empty state's call to action is the only one shown on a phone. The toolbar
carries the same button, and at this width the two sit far enough apart to read
as different controls. Checked every caller first: the CTA-less empty states are
all search-no-match ones, where the search box is right there, so nothing is left
without an action.

Search goes full-width below sm; a fixed 260px overflowed.

The empty region loses some height on narrow — three stacked sections were mostly
whitespace before reaching content.

Not fixed here: the table still compresses rather than scrolling, because it is
`w-full` with no min-width. Five columns in 360px is the real problem and wants a
card-per-row layout below sm — its own change.

Gates: lint 24/24, tsc 0 across ui, settings-ui, oss and mobile.
A full row for one icon button is too much. Reload now stays beside the section
title at every width; only the primary button — wide, labelled — wraps to its own
full-width row below sm, and rejoins the line from sm up.

With the empty state hiding the primary button on a phone, the header collapses
to a single line: title, description, reload.

Only this file is staged — the other changes in the tree are another agent's work
in flight (mobile's tsc is failing on jotai-family resolution and some nav/chat
edits that have nothing to do with this).
…entity-ui

OSSdrillInUIProvider carried 588 lines of workflow-reference wiring that was not
OSS-specific — /m needs the same behaviour to render a config pane, and could not
reach it. It becomes useWorkflowReferenceBridge in the package, and the provider
keeps only the host's own composition.

The bridge imports nothing from @/oss, so it holds in a host that cannot see the
app layer.

Lane: this belongs with the entity-ui work, above pkg/session-surfaces — see
docs/design/sessions-ux-stack/restack-onto-112.md.
Consumes useWorkflowReferenceBridge through a 20-line provider, so /m gets the
reference behaviour the desktop has instead of a second implementation.

Lane: mobile, and it depends on the bridge commit below it.
…eclare it

Six packages imported `atomFamily` from `jotai/utils` while resolving it through
a transitive copy — @agenta/mobile was failing tsc on `Cannot find module
'jotai-family'` because nothing in its graph declared the dep.

Every call site now imports from `jotai-family`, and each package that uses it
lists it. 46 files, one behavioural change: none.

Lane: this is package-wide plumbing and belongs below the settings lanes — see
docs/design/sessions-ux-stack/restack-onto-112.md.
The title rules — normalize, truncate on a character boundary, join the parts —
lived in oss/components/PageTitle/utils and /m had its own `parts` array doing
roughly the same thing. They now share one implementation, with its test.

The surrogate-pair guard is the reason this is worth sharing rather than
reimplementing: a server-persisted session title can arrive cut mid-pair, and the
trailing high surrogate renders as a replacement character. Each host's PageTitle
is now just the <title> element for its router.

Lane: @agenta/shared, below everything else; the oss and mobile rewires ride with
it since the old util is deleted in the same change.
The SDK guards those imports with try/catch, so webpack resolving them is noise.
Production only — dev runs Turbopack.
Both edit sheets loaded the persisted credential into a plain-text control:
`ProviderKeySheet` from `provider.key`, `SecretFormSheet` from
`secret.content`. The vault returns both in the clear — nothing masks them
server-side — so opening either sheet put a live credential on screen.

Start empty in both. The provider key is the value to write, so a save
always replaces what is stored and an empty field saves nothing (the vault's
standard-secret write has no "key unchanged" form). A named secret can be
renamed without retyping its value: empty on edit means keep the stored one,
except when the format changed, since text and a JSON map are not
convertible.
… is the secret

Four defects in the subscribe/edit flow:

- Editing loaded only `event_types[0]` and wrote back a single-element list,
  so a subscription created elsewhere with several types lost the rest on
  any save. The types the sheet has no control for now ride along untouched,
  and the field says how many.
- The one-time reveal fell back to `secret_id` when `secret` was absent.
  `secret_id` names the stored record and verifies no signature, so the user
  would have signed against an identifier and failed. Only `secret` is used;
  a signature subscription that comes back without one says so instead.
- Submit only required a non-empty URL. `type="url"` validates nothing
  outside a submitting form, so `example.com` saved and failed on delivery.
- The copy button reported "Copied" unconditionally: `navigator.clipboard`
  is undefined outside a secure context and `writeText` rejects on denied
  permission. The secret is shown once, so a false "Copied" loses it.
…utlive a tab

`useActiveSettingsTab` accepted any tab this app has a page for, without
asking whether the viewer may see it. Both rails already hide what
`isSettingsTabVisible` denies, so a typed `?tab=tools` was the one way in —
and it landed on writable tool and trigger controls. The router now applies
the same predicate the rails do, and the two cases check it again at the
render boundary.

The confirm sheet is now scoped to the open tab. `TabBody` stays mounted
across tabs, so a destructive action staged on one was still pending on the
next and could be confirmed against a screen the user had left.
…rface token

`--ag-c-FFFFFF` is the legacy hex-named literal; `bg-colorBgContainer` is the
role. Same value in both themes, so nothing moves.
`jotai-family@1` declares `jotai >=2.9.0`. The three packages that pair the
two allowed `jotai >=2.0.0`, a range whose lower half the dependency does not
support.

The lockfile records importer peer specifiers, so it needs regenerating.
@ardaerzin
ardaerzin force-pushed the pkg/settings-ee-pages branch from 9ceda16 to 6f9fe71 Compare August 11, 2026 05:11
…it needs

jotai-family@1.0.1 requires jotai >=2.9.0; twelve packages import it while
nine still advertised >=2.0.0. Raised those nine and regenerated the lockfile,
so --frozen-lockfile stops failing on the importers' peer specifiers.
agenta-annotation-ui keeps >=2.0.0 — it does not use jotai-family.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant