Skip to content

feat(frontend): @agenta/navigation — the sidebar's model leaves the app - #5866

Draft
ardaerzin wants to merge 2 commits into
pkg/authfrom
pkg/navigation
Draft

feat(frontend): @agenta/navigation — the sidebar's model leaves the app#5866
ardaerzin wants to merge 2 commits into
pkg/authfrom
pkg/navigation

Conversation

@ardaerzin

Copy link
Copy Markdown
Contributor

The sidebar knew too much: which items exist, which are gated by edition, which project and
workspace you are in, and where a click should land. All of that lived in OSS components.

@agenta/navigation takes the headless half — the nav item model, the edition and entitlement
gates, project/workspace/org resolution, and the route helpers. No React tree, no antd, so /m
and EE can build a different rail on the same model.
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/auth; 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 10, 2026 2:57pm

Request Review

@coderabbitai

coderabbitai Bot commented Aug 10, 2026

Copy link
Copy Markdown
✅ 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 commented Aug 10, 2026

Copy link
Copy Markdown

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: 6d504bb8-3105-415b-9ae8-690b35ca6409

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 shared navigation infrastructure for sidebar configuration, state, visibility, and dynamic content.
    • Sidebar sessions now support pinned items, recent-session loading, grouping, status indicators, and “Show all” navigation.
    • Dynamic sidebar entries display loading, error, empty, and populated states with configurable icons and limits.
  • Refactor

    • Consolidated sidebar behavior into a shared navigation package for more consistent functionality across the application.

Walkthrough

The PR adds @agenta/navigation as the shared sidebar implementation. It moves sidebar contracts, state, visibility, sessions, dynamic sources, registry logic, and child injection into the package. OSS modules now re-export or wrap the shared APIs.

Changes

Shared Navigation Extraction

Layer / File(s) Summary
Navigation contracts and package boundary
web/packages/agenta-navigation/package.json, web/packages/agenta-navigation/tsconfig.json, web/packages/agenta-navigation/eslint.config.mjs, web/packages/agenta-navigation/src/..., web/oss/src/components/Sidebar/dynamic/types.ts, web/oss/src/components/Sidebar/engine/types.ts
The new package defines shared sidebar and dynamic-entity types, constants, package exports, and headless-package lint rules. OSS type modules re-export these declarations.
Shared state and visibility utilities
web/packages/agenta-navigation/src/state.ts, web/packages/agenta-navigation/src/visibility.ts, web/oss/src/lib/atoms/sidebar.ts, web/oss/src/components/Sidebar/engine/visibility.ts, web/oss/src/components/Sidebar/scopes/constants.ts
The package centralizes persisted collapse state, popup groups, project-scoped open groups, and recursive visibility filtering. OSS modules re-export the shared implementations.
Session sidebar data flow
web/packages/agenta-navigation/src/dynamic/sessionsSource.ts, web/oss/src/components/Sidebar/dynamic/sessionsSource.ts
The shared session source combines recent and pinned session queries, filters invalid sessions, orders pinned entries first, and exposes query status.
Dynamic sources and entity registry
web/packages/agenta-navigation/src/dynamic/source.ts, web/packages/agenta-navigation/src/dynamic/status.ts, web/packages/agenta-navigation/src/dynamic/registry.ts, web/oss/src/components/Sidebar/dynamic/source.ts, web/oss/src/components/Sidebar/dynamic/status.ts, web/oss/src/components/Sidebar/dynamic/registry.ts
The package adds gated source handling, status labels, registered prompt/session/agent entities, and aggregated source state. OSS modules re-export the shared APIs.
Dynamic child resolution and OSS integration
web/packages/agenta-navigation/src/dynamic/useSidebarDynamicChildren.ts, web/oss/src/components/Sidebar/dynamic/useSidebarDynamicChildren.ts
The shared hook resolves loading, error, empty, grouped, and populated states, preserves cached children, and injects dynamic submenus. The OSS wrapper supplies project URLs and entity icons.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant OSSSidebar
  participant useSidebarDynamicChildren
  participant sidebarEntitySourcesAtom
  participant gatedSidebarSource
  participant SessionQueries
  OSSSidebar->>useSidebarDynamicChildren: provide project URL and entity icon resolver
  useSidebarDynamicChildren->>sidebarEntitySourcesAtom: subscribe to registered entities
  sidebarEntitySourcesAtom->>gatedSidebarSource: resolve each gated source
  gatedSidebarSource->>SessionQueries: read session query state when enabled
  SessionQueries-->>gatedSidebarSource: return session data and status
  gatedSidebarSource-->>sidebarEntitySourcesAtom: return source state
  sidebarEntitySourcesAtom-->>useSidebarDynamicChildren: return keyed entity sources
  useSidebarDynamicChildren-->>OSSSidebar: inject resolved dynamic children
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.
Description check ✅ Passed The description directly explains extracting the headless navigation model into @agenta/navigation and provides relevant validation details.
Title check ✅ Passed The title clearly summarizes the main change: moving the sidebar navigation model into @agenta/navigation.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch pkg/navigation

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 left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 6

🧹 Nitpick comments (5)
web/packages/agenta-navigation/src/dynamic/sessionsSource.ts (1)

63-74: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Duplicate label fallback.

toSidebarRef already replaces an empty name with "Untitled session". The registry entry at web/packages/agenta-navigation/src/dynamic/registry.ts line 79 repeats the same fallback with session.name || "Untitled session". Keep the fallback in one place so the copy does not diverge.

web/oss/src/components/Sidebar/dynamic/sessionsSource.ts (1)

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

Re-export only the session symbols.

export * from "@agenta/navigation" re-exports the whole navigation barrel from a module named sessionsSource. Consumers of ./sessionsSource then see unrelated symbols, and duplicate star exports across sibling shims can create ambiguous re-exports. The sibling shims source.ts and status.ts use named re-exports. Use the same pattern here. The same problem exists in web/oss/src/components/Sidebar/dynamic/registry.ts.

♻️ Proposed change
-export * from "`@agenta/navigation`"
+export {sidebarSessionsListAtom, type SessionSidebarRef} from "`@agenta/navigation`"
web/packages/agenta-navigation/src/dynamic/source.ts (1)

44-53: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Read queryAtom once.

Lines 50-52 call get(queryAtom) three times. Extract it into one local for clarity.

♻️ Proposed change
     atom((get) => ({
         data: get(dataAtom),
-        isPending: get(queryAtom).isPending ?? false,
-        isError: get(queryAtom).isError ?? false,
-        error: get(queryAtom).error ?? null,
-    }))
+        ...(({isPending, isError, error}) => ({
+            isPending: isPending ?? false,
+            isError: isError ?? false,
+            error: error ?? null,
+        }))(get(queryAtom)),
+    }))

Alternatively, use a block body with const query = get(queryAtom).

web/packages/agenta-navigation/src/dynamic/registry.ts (1)

3-7: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Remove or track the commented-out entity registrations.

Lines 112-127 hold two commented-out entity definitions. Lines 3 and 6-7 hold the matching commented imports. TESTSETS_SIDEBAR_KEY and EVALUATORS_SIDEBAR_KEY at lines 33-34 are exported but referenced only from that commented code. Dead code in a new shared package is hard to date. Delete it, or add a short TODO that names the follow-up.

Do you want me to open an issue to track re-enabling the test set and evaluator entities?

Also applies to: 33-34, 112-127

web/packages/agenta-navigation/src/dynamic/useSidebarDynamicChildren.ts (1)

141-182: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low value

Cache and effect logic reads correctly.

The memo stays pure and reads only the ref. The effect writes the cache after commit, so Strict Mode's double render cannot corrupt it. One note: kindIcon sits in the memo dependency list at line 166. The OSS binding passes a module-level function, so identity is stable. Document that callers must pass a stable kindIcon, or the memo recomputes on every render.


ℹ️ Review info
⚙️ Run configuration

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

Review profile: CHILL

Plan: Pro Plus

Run ID: 01254659-e79e-49cb-be67-a79de2ebc59e

📥 Commits

Reviewing files that changed from the base of the PR and between 5ee8332 and 581d662.

📒 Files selected for processing (25)
  • web/oss/src/components/Sidebar/dynamic/registry.ts
  • web/oss/src/components/Sidebar/dynamic/sessionsSource.ts
  • web/oss/src/components/Sidebar/dynamic/source.ts
  • web/oss/src/components/Sidebar/dynamic/status.ts
  • web/oss/src/components/Sidebar/dynamic/types.ts
  • web/oss/src/components/Sidebar/dynamic/useSidebarDynamicChildren.ts
  • web/oss/src/components/Sidebar/engine/types.ts
  • web/oss/src/components/Sidebar/engine/visibility.ts
  • web/oss/src/components/Sidebar/scopes/constants.ts
  • web/oss/src/lib/atoms/sidebar.ts
  • web/packages/agenta-navigation/eslint.config.mjs
  • web/packages/agenta-navigation/package.json
  • web/packages/agenta-navigation/src/constants.ts
  • web/packages/agenta-navigation/src/dynamic/index.ts
  • web/packages/agenta-navigation/src/dynamic/registry.ts
  • web/packages/agenta-navigation/src/dynamic/sessionsSource.ts
  • web/packages/agenta-navigation/src/dynamic/source.ts
  • web/packages/agenta-navigation/src/dynamic/status.ts
  • web/packages/agenta-navigation/src/dynamic/types.ts
  • web/packages/agenta-navigation/src/dynamic/useSidebarDynamicChildren.ts
  • web/packages/agenta-navigation/src/index.ts
  • web/packages/agenta-navigation/src/state.ts
  • web/packages/agenta-navigation/src/types.ts
  • web/packages/agenta-navigation/src/visibility.ts
  • web/packages/agenta-navigation/tsconfig.json

}
return sources
})
export * from "@agenta/navigation"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Two OSS shims star-export the entire @agenta/navigation barrel. Both modules run export * from "@agenta/navigation" instead of re-exporting the symbols they own. Any local barrel that re-exports both modules with export * then sees every shared symbol twice, and ES module semantics drop the ambiguous names silently. The sibling shims source.ts and status.ts already use named re-exports.

  • web/oss/src/components/Sidebar/dynamic/registry.ts#L1-L1: replace the star export with named exports of the registry symbols, for example SIDEBAR_ENTITIES, sidebarEntitySourcesAtom, defineSidebarEntity, and the sidebar key constants.
  • web/oss/src/components/Sidebar/dynamic/sessionsSource.ts#L1-L1: replace the star export with named exports of sidebarSessionsListAtom and type SessionSidebarRef.
📍 Affects 2 files
  • web/oss/src/components/Sidebar/dynamic/registry.ts#L1-L1 (this comment)
  • web/oss/src/components/Sidebar/dynamic/sessionsSource.ts#L1-L1

Comment on lines +1 to +6
import {
injectDynamicChildren,
resolveChildren,
useSidebarDynamicChildren as useSharedSidebarDynamicChildren,
type SidebarConfig,
} from "@agenta/navigation"

Copy link
Copy Markdown

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:

#!/bin/bash
# Check workspace dependency wiring for the new navigation package.
fd -t f 'package.json' web -d 4 --exclude node_modules --exec sh -c 'echo "== $1"; jq -r ".name, (.dependencies//{}|keys|join(\", \")), (.peerDependencies//{}|keys|join(\", \"))" "$1"' _ {} \
  | rg -n -A3 'agenta-navigation|`@agenta/oss`|`@agenta/navigation`'
echo "--- imports of `@agenta/navigation` ---"
rg -n '`@agenta/navigation`' web --glob '!**/node_modules/**'

Repository: Agenta-AI/agenta

Length of output: 50372


🏁 Script executed:

#!/bin/bash
set -u

echo "== relevant package.json files =="
for f in web/oss/package.json web/packages/agenta-navigation/package.json; do
  if [ -f "$f" ]; then
    echo "== $f =="
    cat "$f"
  else
    echo "MISSING $f"
  fi
done

echo
echo "== targeted dependency/peer declarations =="
python3 - <<'PY'
import json, pathlib

files = [
    "web/oss/package.json",
    "web/packages/agenta-navigation/package.json"
]
for path in files:
    p = pathlib.Path(path)
    if not p.exists():
        print(f"MISSING {path}")
        continue
    data = json.loads(p.read_text())
    print(f"== {path} ==")
    print("name:", data.get("name"))
    for sec in ["dependencies", "peerDependencies", "devDependencies"]:
        deps = data.get(sec) or {}
        hits = {k: v for k, v in deps.items() if k in [
            "`@agenta/navigation`", "`@agenta/oss`", "`@agenta/entities`",
            "`@agenta/sessions`", "`@agenta/shared`", "jotai",
            "jotai-tanstack-query", "react", "`@phosphor-icons/react`"
        ] or k.startswith("`@agenta/`") or k == "typescript"]
        if hits:
            print(sec, hits)
PY

echo
echo "== `@agenta/navigation` imports =="
rg -n --fixed-strings '`@agenta/navigation`' web --glob '!**/node_modules/**' | sed -n '1,120p'

Repository: Agenta-AI/agenta

Length of output: 9341


Add @agenta/navigation to the OSS dependency list.

web/oss/package.json imports @agenta/navigation through web/oss/src/lib/atoms/sidebar.ts, but the package does not declare it. Add the workspace dependency so OSS installs without relying on root/host package hoisting.

Comment on lines +17 to +35
"dependencies": {
"@agenta/entities": "workspace:../agenta-entities",
"@agenta/sessions": "workspace:../agenta-sessions",
"@agenta/shared": "workspace:../agenta-shared",
"@phosphor-icons/react": "^2.1.10",
"jotai-tanstack-query": "^0.11.0"
},
"peerDependencies": {
"@tanstack/react-query": ">=5.0.0",
"jotai": ">=2.0.0",
"react": ">=18.0.0"
},
"devDependencies": {
"@tanstack/react-query": "5.100.9",
"@types/node": "^20.19.20",
"@types/react": "^19.0.10",
"jotai": "^2.15.0",
"react": "^19.0.0",
"typescript": "^5.9.3"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

jq '{dependencies, peerDependencies, devDependencies}' \
  web/packages/agenta-navigation/package.json

rg -n \
  -g '*.ts' -g '*.tsx' \
  'from "react"|`@phosphor-icons/react`|ReactNode|ComponentType|MouseEvent|createElement' \
  web/packages/agenta-navigation/src

Repository: Agenta-AI/agenta

Length of output: 2987


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '--- package files mentioning agenta-navigation ---\n'
rg -n '"`@agenta/navigation`"|"agenta-navigation"|agenta-navigation' web -g 'package.json' -g 'pnpm-lock.yaml' || true

printf '\n--- files importing `@agenta/navigation` ---\n'
rg -n 'from ["'\'']`@agenta/navigation`["'\'']|from ["'\'']`@agenta/navigation/`' web -g '*.ts' -g '*.tsx' || true

printf '\n--- files importing agenta-navigation src types ---\n'
rg -n 'from ["'\'']\.?/?packages/agenta-navigation/src/(types|dynamic/types)["'\'']|from ["'\''].+\\.\.*\\./packages/agenta-navigation/src/' web -g '*.ts' -g '*.tsx' || true

printf '\n--- package workspace top files ---\n'
for f in web/pnpm-workspace.yaml web/package.json web/packages/agenta-navigation/package.json; do
  if [ -f "$f" ]; then
    echo "### $f"
    sed -n '1,220p' "$f"
  fi
done

Repository: Agenta-AI/agenta

Length of output: 245


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- package files mentioning agenta-navigation ---'
rg -n '"`@agenta/navigation`"|"agenta-navigation"|agenta-navigation' web -g 'package.json' -g 'pnpm-lock.yaml' || true

printf '%s\n' '';
printf '%s\n' '--- files importing `@agenta/navigation` ---'
rg -n 'from ["'\'']`@agenta/navigation`["'\'']|from ["'\'']`@agenta/navigation/`' web -g '*.ts' -g '*.tsx' || true

printf '%s\n' '';
printf '%s\n' '--- files importing agenta-navigation src types ---'
rg -n 'from ["'\'']\.\/?packages/agenta-navigation/src/(types|dynamic/types)["'\'']|from ["'\''].+\\.\.*\\./packages/agenta-navigation/src/' web -g '*.ts' -g '*.tsx' || true

printf '%s\n' '';
printf '%s\n' '--- workspace/package snippets ---'
for f in web/pnpm-workspace.yaml web/package.json web/packages/agenta-navigation/package.json; do
  if [ -f "$f" ]; then
    echo "### $f"
    sed -n '1,220p' "$f"
  fi
done

Repository: Agenta-AI/agenta

Length of output: 8583


Keep @agenta/navigation framework-neutral.

This package is private, but its public model is still React-coupled. The shared interfaces expose React rendering types, event types, and React-only dependencies; React rendering should stay in OSS/EE adapter packages.

  • web/packages/agenta-navigation/package.json#L17-L35: keep React dependencies out of the navigation model unless downstream exports require React as a consumer peer/dependency.
  • web/packages/agenta-navigation/src/types.ts#L1-L83: remove React-specific ReactNode, ComponentType, and React MouseEvent contract details from shared navigation types.
  • web/packages/agenta-navigation/src/dynamic/types.ts#L1-L89: replace ReactNode icon return callbacks with framework-neutral icon metadata or adapter callbacks.
📍 Affects 3 files
  • web/packages/agenta-navigation/package.json#L17-L35 (this comment)
  • web/packages/agenta-navigation/src/types.ts#L1-L83
  • web/packages/agenta-navigation/src/dynamic/types.ts#L1-L89

Comment on lines +75 to +102
defineSidebarEntity<SessionSidebarRef>(MAIN_SIDEBAR_SCOPE_ID, SESSIONS_SIDEBAR_KEY, {
kind: "app",
icon: createElement(ChatsCircleIcon, {size: 14}),
listAtom: sidebarSessionsListAtom,
getLabel: (session) => session.name || "Untitled session",
// The link navigates to the owning agent; the click hands over WHICH session, since the
// playground has no way to read that from the route.
childPath: (session) => `/apps/${session.appId}/playground`,
getOnClick: (session) => () => {
if (!session.appId) return
getDefaultStore().set(pendingSessionOpenAtom, {
appId: session.appId,
sessionId: session.sessionId,
title: session.name ?? undefined,
})
},
getGroup: (session) => (session.pinned ? "Pinned" : null),
getIcon: (session) =>
session.pinned
? createElement(PushPinIcon, {size: 14, weight: "fill"})
: createElement(CircleIcon, {
size: 10,
weight: session.alive ? "fill" : "regular",
}),
emptyLabel: "No sessions",
maxItems: 7,
showAllPath: "/sessions",
}),

Copy link
Copy Markdown

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

A session with a null appId renders a link to /apps/null/playground.

SessionSidebarRef.appId is string | null. childPath at line 82 interpolates it without a guard. toSidebarRef in sessionsSource.ts only drops rows when sessionOpenTarget(row) returns falsy; it keeps rows whose target.appId is null. getOnClick guards with if (!session.appId) return, but resolveChildren still assigns link, so the row remains clickable and navigates to a broken route. Drop these rows in toSidebarRef, or narrow appId to string in SessionSidebarRef.

🐛 Proposed fix in web/packages/agenta-navigation/src/dynamic/sessionsSource.ts
 const toSidebarRef = (row: SessionStream, pinned: Set<string>): SessionSidebarRef | null => {
     const target = sessionOpenTarget(row)
-    if (!target) return null
+    if (!target?.appId) return null

Then narrow the field:

 export interface SessionSidebarRef extends SidebarEntityRef {
     sessionId: string
-    appId: string | null
+    appId: string
📝 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
defineSidebarEntity<SessionSidebarRef>(MAIN_SIDEBAR_SCOPE_ID, SESSIONS_SIDEBAR_KEY, {
kind: "app",
icon: createElement(ChatsCircleIcon, {size: 14}),
listAtom: sidebarSessionsListAtom,
getLabel: (session) => session.name || "Untitled session",
// The link navigates to the owning agent; the click hands over WHICH session, since the
// playground has no way to read that from the route.
childPath: (session) => `/apps/${session.appId}/playground`,
getOnClick: (session) => () => {
if (!session.appId) return
getDefaultStore().set(pendingSessionOpenAtom, {
appId: session.appId,
sessionId: session.sessionId,
title: session.name ?? undefined,
})
},
getGroup: (session) => (session.pinned ? "Pinned" : null),
getIcon: (session) =>
session.pinned
? createElement(PushPinIcon, {size: 14, weight: "fill"})
: createElement(CircleIcon, {
size: 10,
weight: session.alive ? "fill" : "regular",
}),
emptyLabel: "No sessions",
maxItems: 7,
showAllPath: "/sessions",
}),
const toSidebarRef = (row: SessionStream, pinned: Set<string>): SessionSidebarRef | null => {
const target = sessionOpenTarget(row)
if (!target?.appId) return null
Suggested change
defineSidebarEntity<SessionSidebarRef>(MAIN_SIDEBAR_SCOPE_ID, SESSIONS_SIDEBAR_KEY, {
kind: "app",
icon: createElement(ChatsCircleIcon, {size: 14}),
listAtom: sidebarSessionsListAtom,
getLabel: (session) => session.name || "Untitled session",
// The link navigates to the owning agent; the click hands over WHICH session, since the
// playground has no way to read that from the route.
childPath: (session) => `/apps/${session.appId}/playground`,
getOnClick: (session) => () => {
if (!session.appId) return
getDefaultStore().set(pendingSessionOpenAtom, {
appId: session.appId,
sessionId: session.sessionId,
title: session.name ?? undefined,
})
},
getGroup: (session) => (session.pinned ? "Pinned" : null),
getIcon: (session) =>
session.pinned
? createElement(PushPinIcon, {size: 14, weight: "fill"})
: createElement(CircleIcon, {
size: 10,
weight: session.alive ? "fill" : "regular",
}),
emptyLabel: "No sessions",
maxItems: 7,
showAllPath: "/sessions",
}),
export interface SessionSidebarRef extends SidebarEntityRef {
sessionId: string
appId: string

Comment on lines +21 to +22
const inlineOpen = (get(sidebarOpenGroupsAtomFamily(scopeId)) ?? []).includes(parentKey)
const popupOpen = get(sidebarPopupGroupsAtomFamily(scopeId)).includes(parentKey)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Inconsistent nullish guard between the two group families.

Line 21 guards sidebarOpenGroupsAtomFamily(scopeId) with ?? []. Line 22 reads sidebarPopupGroupsAtomFamily(scopeId) without a guard and calls .includes directly. If either family can hold null or undefined, line 22 throws a TypeError. Apply the same guard to both, or drop it from both if the state atoms always hold arrays.

🛡️ Proposed change
-        const popupOpen = get(sidebarPopupGroupsAtomFamily(scopeId)).includes(parentKey)
+        const popupOpen = (get(sidebarPopupGroupsAtomFamily(scopeId)) ?? []).includes(parentKey)
📝 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
const inlineOpen = (get(sidebarOpenGroupsAtomFamily(scopeId)) ?? []).includes(parentKey)
const popupOpen = get(sidebarPopupGroupsAtomFamily(scopeId)).includes(parentKey)
const inlineOpen = (get(sidebarOpenGroupsAtomFamily(scopeId)) ?? []).includes(parentKey)
const popupOpen = (get(sidebarPopupGroupsAtomFamily(scopeId)) ?? []).includes(parentKey)

Comment on lines +3 to +4
// Drop hidden entries at every level, not just the section root, so nested hidden items
// never render, auto-open, or become selected.

Copy link
Copy Markdown

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

Use one short implementation comment.

Lines 3-4 add a two-line comment for ordinary filtering logic. Replace it with one short line or remove it. As per coding guidelines, “Keep in-code comments to at most one short line; use longer comments only for genuinely surprising constraints such as bugs, races, or ordering requirements.”

Source: Coding guidelines

The ref is only built from a resolved sessionOpenTarget, whose appId is a
UUID-checked string, so the `| null` was a widening that made childPath
capable of emitting /apps/null/playground and forced a dead guard in the
sessions entity's getOnClick. Narrow the type and drop the guard.
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