From ca50868efec967efa40d630ea622b5972f3a4724 Mon Sep 17 00:00:00 2001 From: pavelhov Date: Thu, 13 Aug 2026 01:40:51 -0400 Subject: [PATCH] GUI: rename Active Roster to Configured Roster with protocol exceptions The Subagents page presented the union of the V1/V2 collaboration-surface projections as "Active Roster", which can show a model (e.g. Luna) as active even though a V2 parent's spawn_agent schema cannot select it under Codex defaults. - Rename the card to "Configured Roster" and derive per-row collaboration reachability from the existing catalog projections (fail-closed; only server-observed roster rows get a claim). - Show status text only for exceptions: V1 under Codex defaults / V2 only / Routed V2 compatible / Not advertised; rows on both surfaces get screen-reader text only. - Add one split notice with a focus-only "Use Concurrent V2" action and a closed "Protocol diagnostics" disclosure. - Update i18n (en/de/ja/ko/ru/zh), docs-site guides, and add unit tests. - Server/API contract unchanged. Verified: bun run typecheck, bun run test (594 shards), bun run lint:gui, bun run privacy:scan, bun run build:gui. --- .../content/docs/guides/codex-app-models.md | 2 +- .../src/content/docs/guides/model-ordering.md | 2 +- .../content/docs/guides/sub-agent-surface.md | 7 +- .../docs/ja/guides/codex-app-models.md | 2 +- .../content/docs/ja/guides/model-ordering.md | 2 +- .../docs/ja/guides/sub-agent-surface.md | 2 +- .../docs/ko/guides/codex-app-models.md | 2 +- .../content/docs/ko/guides/model-ordering.md | 2 +- .../docs/ko/guides/sub-agent-surface.md | 2 +- .../docs/ru/guides/codex-app-models.md | 2 +- .../content/docs/ru/guides/model-ordering.md | 2 +- .../docs/ru/guides/sub-agent-surface.md | 2 +- .../docs/zh-cn/guides/codex-app-models.md | 2 +- .../docs/zh-cn/guides/model-ordering.md | 2 +- .../docs/zh-cn/guides/sub-agent-surface.md | 2 +- .../SubagentsWorkspace.tsx | 109 ++++++++- gui/src/i18n/de.ts | 24 +- gui/src/i18n/en.ts | 24 +- gui/src/i18n/ja.ts | 24 +- gui/src/i18n/ko.ts | 24 +- gui/src/i18n/ru.ts | 24 +- gui/src/i18n/zh.ts | 24 +- gui/src/pages/Subagents.tsx | 38 +++ gui/src/pages/subagent-roster-reachability.ts | 86 +++++++ gui/src/styles-subagents-workspace.css | 111 +++++++++ tests/subagent-roster-reachability.test.ts | 218 ++++++++++++++++++ 26 files changed, 697 insertions(+), 44 deletions(-) create mode 100644 gui/src/pages/subagent-roster-reachability.ts create mode 100644 tests/subagent-roster-reachability.test.ts diff --git a/docs-site/src/content/docs/guides/codex-app-models.md b/docs-site/src/content/docs/guides/codex-app-models.md index e6549004e5..0ca894ffb8 100644 --- a/docs-site/src/content/docs/guides/codex-app-models.md +++ b/docs-site/src/content/docs/guides/codex-app-models.md @@ -166,7 +166,7 @@ actually advertised, and assigns low catalog priorities in the selected order. W are active, bare native selections expand into selector-qualified groups. Other models remain callable by exact id. -The active roster is separate from the Dashboard's **Sub-agent delegation** selection. It +The configured roster is separate from the Dashboard's **Sub-agent delegation** selection. It controls which overrides Codex offers first; it does not select a model or trigger delegation by itself. diff --git a/docs-site/src/content/docs/guides/model-ordering.md b/docs-site/src/content/docs/guides/model-ordering.md index 05b291b3a2..113a4e0cda 100644 --- a/docs-site/src/content/docs/guides/model-ordering.md +++ b/docs-site/src/content/docs/guides/model-ordering.md @@ -109,7 +109,7 @@ have expanded into selector-qualified groups. ## Changing the order The supported way to customize leading model order is to reorder `subagentModels`. In the dashboard, -open **Subagents** → **Agent Command Center** and reorder the **Active Roster** by dragging, with the +open **Subagents** → **Agent Command Center** and reorder the **Configured Roster** by dragging, with the arrow buttons, or with Alt + /. The searchable **Agent Library** may contain far more than five catalog models; entries remain addressable by exact id when their route is available, while the five-slot limit applies only to the overrides advertised first to `spawn_agent`. diff --git a/docs-site/src/content/docs/guides/sub-agent-surface.md b/docs-site/src/content/docs/guides/sub-agent-surface.md index 111f804116..bfd405e80a 100644 --- a/docs-site/src/content/docs/guides/sub-agent-surface.md +++ b/docs-site/src/content/docs/guides/sub-agent-surface.md @@ -156,8 +156,11 @@ switching an active conversation in place. - **Dashboard** → first stat cell: choose **v1**, **base**, or **v2**. - **Models** → **Current behavior** → **Collaboration**: choose **Reliable v1**, **Codex native** (base/default semantics), or **Concurrent v2**. - **Subagents** → **Agent Command Center**: - - **Active Roster** chooses and orders the five model overrides advertised first to `spawn_agent`. - Drag rows, use the arrow buttons, or press Alt + /. + - **Configured Roster** chooses and orders the five model overrides advertised first to `spawn_agent`. + Drag rows, use the arrow buttons, or press Alt + /. The card + shows your configured quick picks: a row's status line (for example **V1 under Codex defaults** or + **Routed · V2 compatible**) indicates which collaboration surfaces can select it, while rows + without a status line are available on both. - **Agent Library** searches the current model catalog and filters factual capabilities such as reasoning, long context, vision, and tool support. Entries remain addressable by exact id when their route is available, including models outside the five-slot roster. diff --git a/docs-site/src/content/docs/ja/guides/codex-app-models.md b/docs-site/src/content/docs/ja/guides/codex-app-models.md index ab06535c3d..f4d58c4cee 100644 --- a/docs-site/src/content/docs/ja/guides/codex-app-models.md +++ b/docs-site/src/content/docs/ja/guides/codex-app-models.md @@ -100,7 +100,7 @@ fast_mode = true Codex は、ピッカーに表示されるカタログ エントリを `priority` の昇順で並べ替え、最初の 5 つを `spawn_agent` モデル オーバーライドとしてアドバタイズします。ダッシュボードの **Agent Command Center** では、bare native id または routed `provider/model` id を最大 5 つ選択して保存できます。設定済みの account-qualified `/` id も保持され、各保存項目が実際に公開されたか除外されたかが表示されます。CodexCommander は選択順に低いカタログ priority を割り当てます。account selector が有効な場合、bare native の選択は selector-qualified グループに展開されます。他のモデルは引き続き正確な ID で呼び出すことができます。 -Active Roster は、ダッシュボードの **サブエージェント委任** の選択とは別のものです。Codex が最初に提供する override を制御しますが、モデルを選択したり、委任をトリガーしたりすることはありません。 +設定済みロースターは、ダッシュボードの **サブエージェント委任** の選択とは別のものです。Codex が最初に提供する override を制御しますが、モデルを選択したり、委任をトリガーしたりすることはありません。 ## モデルの状態を更新しています diff --git a/docs-site/src/content/docs/ja/guides/model-ordering.md b/docs-site/src/content/docs/ja/guides/model-ordering.md index f23e68525e..29b95bcd56 100644 --- a/docs-site/src/content/docs/ja/guides/model-ordering.md +++ b/docs-site/src/content/docs/ja/guides/model-ordering.md @@ -106,7 +106,7 @@ account selector がある場合、5 項目の制限は bare native の選択が 先頭モデルの順序を変更するサポート手段は、`subagentModels` を並べ替えることです。ダッシュボードでは **Subagents** → **Agent Command Center** を開き、ドラッグ、矢印ボタン、または -Alt + / で **Active Roster** を並べ替えます。検索可能な +Alt + / で **設定済みロースター** を並べ替えます。検索可能な **Agent Library** には 5 つをはるかに超えるカタログモデルが含まれる場合があります。ルートが利用可能な 場合にエントリを正確な id で指定でき、5 枠の制限は `spawn_agent` に最初に公開されるオーバーライドにのみ適用されます。 diff --git a/docs-site/src/content/docs/ja/guides/sub-agent-surface.md b/docs-site/src/content/docs/ja/guides/sub-agent-surface.md index 5492ba4df5..981e79ae63 100644 --- a/docs-site/src/content/docs/ja/guides/sub-agent-surface.md +++ b/docs-site/src/content/docs/ja/guides/sub-agent-surface.md @@ -95,7 +95,7 @@ CodexCommander は、空のタスクまたは読み取り不可能なタスク - **ダッシュボード** → 最初の統計セル: **v1**、**base**、または **v2** を選択します。 - **モデル** → **現在の動作** → **コラボレーション**: **Reliable v1**、**Codex native**(base/default の意味)、**Concurrent v2** から選択します。 - **サブエージェント** → **Agent Command Center**: - - **Active Roster** は、`spawn_agent` に最初に公開される 5 つのモデル オーバーライドを選択して順序付けします。行をドラッグするか、矢印ボタンを使うか、Alt + / を押します。 + - **設定済みロースター** は、`spawn_agent` に最初に公開される 5 つのモデル オーバーライドを選択して順序付けします。行をドラッグするか、矢印ボタンを使うか、Alt + / を押します。 - **Agent Library** は現在のモデルカタログを検索し、reasoning、long context、vision、tool support などの事実上の機能でフィルタリングします。ルートが利用可能な場合、5 枠のロスター外のエントリも正確な id で指定できます。 - **Run Policy** は、エージェントプロトコル、V2 メッセージ配信、優先ガイダンスモデルと effort、生成された子タスク用のグローバルフォールバック、ヘルス再確認間隔、スレッド制限、サブエージェント effort 上限、ロースターガイダンス、ネイティブ Codex デフォルト同期をステージングします。ポリシーの変更はロースターの変更とは別に保存します。 diff --git a/docs-site/src/content/docs/ko/guides/codex-app-models.md b/docs-site/src/content/docs/ko/guides/codex-app-models.md index e0782c53ce..f664166c96 100644 --- a/docs-site/src/content/docs/ko/guides/codex-app-models.md +++ b/docs-site/src/content/docs/ko/guides/codex-app-models.md @@ -143,7 +143,7 @@ routed `provider/model` id를 최대 다섯 개 선택하고 저장할 수 있 CodexCommander는 선택한 순서대로 낮은 카탈로그 priority를 부여합니다. account selector가 활성화되어 있으면 bare native 선택은 selector-qualified 그룹으로 확장됩니다. 다른 모델도 정확한 id로 직접 호출할 수 있습니다. -Active Roster는 Dashboard의 **Sub-agent delegation** 선택과 별개입니다. Codex가 먼저 보여 줄 +설정된 로스터는 Dashboard의 **Sub-agent delegation** 선택과 별개입니다. Codex가 먼저 보여 줄 override를 정할 뿐, 모델을 고르거나 delegation을 시작하지는 않습니다. ## 모델 상태 새로고침 diff --git a/docs-site/src/content/docs/ko/guides/model-ordering.md b/docs-site/src/content/docs/ko/guides/model-ordering.md index f7d0b68717..10bfc36ea5 100644 --- a/docs-site/src/content/docs/ko/guides/model-ordering.md +++ b/docs-site/src/content/docs/ko/guides/model-ordering.md @@ -106,7 +106,7 @@ account selector가 있으면 bare native 선택이 selector-qualified 그룹으 선두 모델 순서를 바꾸는 지원 수단은 `subagentModels`를 재정렬하는 것입니다. 대시보드에서 **Subagents** → **Agent Command Center**를 열고 드래그, 화살표 버튼, 또는 -Alt + /로 **Active Roster**의 순서를 바꿉니다. 검색 가능한 +Alt + /로 **설정된 로스터**의 순서를 바꿉니다. 검색 가능한 **Agent Library**에는 다섯 개를 훨씬 넘는 카탈로그 모델이 있을 수 있습니다. 라우트가 사용 가능할 때 항목을 정확한 id로 지정할 수 있으며, 다섯 칸 제한은 `spawn_agent`에 가장 먼저 알려지는 오버라이드에만 적용됩니다. diff --git a/docs-site/src/content/docs/ko/guides/sub-agent-surface.md b/docs-site/src/content/docs/ko/guides/sub-agent-surface.md index 89c5ad0f1b..b067332f5d 100644 --- a/docs-site/src/content/docs/ko/guides/sub-agent-surface.md +++ b/docs-site/src/content/docs/ko/guides/sub-agent-surface.md @@ -93,7 +93,7 @@ CodexCommander는 읽을 수 없거나 빈 작업을 그대로 넘기지 않고 - **Dashboard** → 첫 번째 상태 셀: **v1**, **base**, **v2**를 고릅니다. - **Models** → **Current behavior** → **Collaboration**: **Reliable v1**, **Codex native**(base/default 의미), **Concurrent v2** 중에서 고릅니다. - **Subagents** → **Agent Command Center**: - - **Active Roster**는 `spawn_agent`에 가장 먼저 알려지는 다섯 개의 모델 오버라이드를 선택하고 순서를 정합니다. 행을 드래그하거나, 화살표 버튼을 쓰거나, Alt + /를 누르세요. + - **설정된 로스터**는 `spawn_agent`에 가장 먼저 알려지는 다섯 개의 모델 오버라이드를 선택하고 순서를 정합니다. 행을 드래그하거나, 화살표 버튼을 쓰거나, Alt + /를 누르세요. - **Agent Library**는 현재 모델 카탈로그를 검색하고 reasoning, long context, vision, tool support 같은 사실 기반 capability로 필터링합니다. 라우트가 사용 가능하면 다섯 칸 로스터 밖의 항목도 정확한 id로 지정할 수 있습니다. - **Run Policy**는 에이전트 프로토콜, V2 메시지 전달, 선호 안내 모델과 강도, 생성된 하위 작업의 전역 폴백 체인, 헬스 재확인 간격, 스레드 제한, 서브에이전트 강도 상한, 로스터 안내, 네이티브 Codex 기본값 동기화를 스테이징합니다. 정책 변경은 로스터 변경과 별도로 저장하세요. diff --git a/docs-site/src/content/docs/ru/guides/codex-app-models.md b/docs-site/src/content/docs/ru/guides/codex-app-models.md index 2ad1e6f2bb..3512f75d9e 100644 --- a/docs-site/src/content/docs/ru/guides/codex-app-models.md +++ b/docs-site/src/content/docs/ru/guides/codex-app-models.md @@ -152,7 +152,7 @@ account-qualified id `/` сохраняются, каталога в выбранном порядке; при активных селекторах аккаунтов bare native-выбор разворачивается в группы selector-qualified строк. Остальные модели всё равно можно вызывать по точному id. -Active Roster отделён от выбора **Sub-agent delegation** в дашборде. Он только +Настроенный состав отделён от выбора **Sub-agent delegation** в дашборде. Он только определяет, какие override Codex показывает первыми; он не выбирает модель и не инициирует делегирование сам по себе. diff --git a/docs-site/src/content/docs/ru/guides/model-ordering.md b/docs-site/src/content/docs/ru/guides/model-ordering.md index d63b24fe87..db06adf4b7 100644 --- a/docs-site/src/content/docs/ru/guides/model-ordering.md +++ b/docs-site/src/content/docs/ru/guides/model-ordering.md @@ -114,7 +114,7 @@ native-выбора в selector-qualified группы. ## Изменение порядка Поддерживаемый способ настроить порядок ведущих моделей — переставить элементы `subagentModels`. -В дашборде откройте **Subagents** → **Agent Command Center** и измените порядок **Active Roster** +В дашборде откройте **Subagents** → **Agent Command Center** и измените порядок **Настроенный состав** перетаскиванием, кнопками со стрелками или Alt + /. В поисковой **Agent Library** может содержать гораздо больше пяти моделей каталога; запись остаётся адресуемой по точному id, если доступен её маршрут, а ограничение в пять слотов применяется только к переопределениям, diff --git a/docs-site/src/content/docs/ru/guides/sub-agent-surface.md b/docs-site/src/content/docs/ru/guides/sub-agent-surface.md index 62e2dd8611..27dceec714 100644 --- a/docs-site/src/content/docs/ru/guides/sub-agent-surface.md +++ b/docs-site/src/content/docs/ru/guides/sub-agent-surface.md @@ -135,7 +135,7 @@ combo, использовать v1 для делегирования между - **Dashboard** → первая ячейка со статистикой: выберите **v1**, **base** или **v2**. - **Models** → **Current behavior** → **Collaboration**: выберите **Reliable v1**, **Codex native** (семантика base/default) или **Concurrent v2**. - **Subagents** → **Agent Command Center**: - - **Active Roster** выбирает и упорядочивает пять переопределений моделей, первыми объявляемых + - **Настроенный состав** выбирает и упорядочивает пять переопределений моделей, первыми объявляемых `spawn_agent`. Перетаскивайте строки, используйте кнопки со стрелками или нажимайте Alt + /. - **Agent Library** ищет текущий каталог моделей и фильтрует фактические возможности, такие как diff --git a/docs-site/src/content/docs/zh-cn/guides/codex-app-models.md b/docs-site/src/content/docs/zh-cn/guides/codex-app-models.md index 06620c27bf..64bee6089b 100644 --- a/docs-site/src/content/docs/zh-cn/guides/codex-app-models.md +++ b/docs-site/src/content/docs/zh-cn/guides/codex-app-models.md @@ -97,7 +97,7 @@ fast_mode = true Codex 会按 `priority` 升序对选择器可见的目录条目排序,并把前五个作为 `spawn_agent` 模型 override 暴露出来。仪表盘的 **Agent Command Center** 最多可以选择并保存五个裸原生 id 或路由 `provider/model` id,也会保留已配置的账户限定 `/` id,并报告每个保存项是否实际公开或被排除。CodexCommander 会按所选顺序分配较低的目录 priority;启用账户 selector 时,裸原生选择会展开为 selector-qualified 分组。其他模型仍然可以通过精确 id 调用。 -Active Roster 与 Dashboard 的 **Sub-agent delegation** 选择彼此独立。它只决定 Codex 先提供哪些 override;它不会自己选择模型,也不会触发委派。 +已配置名单与 Dashboard 的 **Sub-agent delegation** 选择彼此独立。它只决定 Codex 先提供哪些 override;它不会自己选择模型,也不会触发委派。 ## 刷新模型状态 diff --git a/docs-site/src/content/docs/zh-cn/guides/model-ordering.md b/docs-site/src/content/docs/zh-cn/guides/model-ordering.md index f1259c793a..c10623a80c 100644 --- a/docs-site/src/content/docs/zh-cn/guides/model-ordering.md +++ b/docs-site/src/content/docs/zh-cn/guides/model-ordering.md @@ -102,7 +102,7 @@ subagentModels = [ 自定义开头模型顺序的受支持方式是重新排列 `subagentModels`。在仪表盘中打开 **Subagents** → **Agent Command Center**,通过拖动、箭头按钮或 Alt + / 重新排列 -**Active Roster**。可搜索的 **Agent Library** 可能包含远超五个的目录模型;路由可用时条目仍可通过精确 id +**已配置名单**。可搜索的 **Agent Library** 可能包含远超五个的目录模型;路由可用时条目仍可通过精确 id 指定,而五个槽位的限制仅适用于最先向 `spawn_agent` 公布的 override。 使用 `ccx agent subagents set` 或编辑 CodexCommander 配置,添加实时库中没有的精确 diff --git a/docs-site/src/content/docs/zh-cn/guides/sub-agent-surface.md b/docs-site/src/content/docs/zh-cn/guides/sub-agent-surface.md index 266701e430..cf8448ac42 100644 --- a/docs-site/src/content/docs/zh-cn/guides/sub-agent-surface.md +++ b/docs-site/src/content/docs/zh-cn/guides/sub-agent-surface.md @@ -93,7 +93,7 @@ CodexCommander 会安全失败,而不是转发空任务或不可读任务: - **Dashboard** → 第一个状态单元:选择 **v1**、**base** 或 **v2**。 - **Models** → **Current behavior** → **Collaboration**:选择 **Reliable v1**、**Codex native**(base/default 语义)或 **Concurrent v2**。 - **Subagents** → **Agent Command Center**: - - **Active Roster** 选择并排序最先向 `spawn_agent` 公布的五个模型 override。拖动行、使用箭头按钮,或按 Alt + /。 + - **已配置名单** 选择并排序最先向 `spawn_agent` 公布的五个模型 override。拖动行、使用箭头按钮,或按 Alt + /。 - **Agent Library** 搜索当前模型目录,并按事实能力进行筛选,例如推理、长上下文、视觉和工具支持。路由可用时,五个槽位 roster 之外的条目仍可通过精确 id 指定。 - **Run Policy** 暂存代理协议、V2 消息传递、首选指导模型和 effort、已生成子任务的全局回退链、健康复查间隔、线程限制、子代理 effort 上限、名单指导,以及原生 Codex 默认值同步。策略变更与 roster 变更分开保存。 diff --git a/gui/src/components/subagents-workspace/SubagentsWorkspace.tsx b/gui/src/components/subagents-workspace/SubagentsWorkspace.tsx index 3041377718..69ae3732e7 100644 --- a/gui/src/components/subagents-workspace/SubagentsWorkspace.tsx +++ b/gui/src/components/subagents-workspace/SubagentsWorkspace.tsx @@ -10,11 +10,12 @@ import { IconSearch, IconX, } from "../../icons"; -import { useI18n, useT, type Locale } from "../../i18n/shared"; +import { useI18n, useT, type Locale, type TFn } from "../../i18n/shared"; import { modelLabel } from "../../model-display"; import { formatNamespacedModelId, providerIconSrc } from "../../provider-icons"; import SubagentDelegationSection from "./SubagentDelegationSection"; import type { DelegationPatch, DelegationModelOption } from "../../pages/use-subagent-delegation"; +import type { RosterReachability } from "../../pages/subagent-roster-reachability"; export const FEATURED_MAX = 5; export const LONG_CONTEXT_MIN = 200_000; @@ -46,6 +47,9 @@ export interface SubagentsWorkspaceProps { available: string[]; models: AgentModelRow[]; chosen: string[]; + protocol?: "v1" | "default" | "v2" | null; + rosterReachability?: ReadonlyMap; + onUseConcurrentV2?: () => void; busy?: boolean; rosterDirty?: boolean; onToggle: (model: string) => void; @@ -135,10 +139,22 @@ function ModelChips({ model }: { model: AgentModelRow }) { ); } +function surfaceLabel(surface: RosterReachability, t: TFn): string { + switch (surface) { + case "both": return t("sub.roster.surface.both"); + case "v1": return t("sub.roster.surface.v1"); + case "v2": return t("sub.roster.surface.v2"); + case "neither": return t("sub.roster.surface.none"); + } +} + export default function SubagentsWorkspace({ available, models, chosen, + protocol, + rosterReachability, + onUseConcurrentV2, busy = false, rosterDirty = false, onToggle, @@ -213,14 +229,37 @@ export default function SubagentsWorkspace({ { id: "tools", label: t("sub.filter.tools") }, ]; + // Per-selector collaboration-surface exceptions among the chosen rows. + const rosterStatus = useMemo(() => { + const v1: string[] = []; + const v2: string[] = []; + if (rosterReachability) { + for (const selector of chosen) { + const state = rosterReachability.get(selector); + if (state === "v1") v1.push(selector); + else if (state === "v2") v2.push(selector); + } + } + return { v1, v2 }; + }, [chosen, rosterReachability]); + + // One notice, only when the legacy "default" protocol splits the roster + // across surfaces. The gate is inherently map-driven: rosterStatus only + // collects entries from a non-empty rosterReachability map, so an empty or + // missing map can never produce a notice. "neither" rows are deliberately + // not listed by name. Names stay in roster order. + const showSplitNotice = protocol === "default" && (rosterStatus.v1.length > 0 || rosterStatus.v2.length > 0); + const v1Names = rosterStatus.v1.map(selector => formatNamespacedModelId(selector, t)).join(", "); + const v2Names = rosterStatus.v2.map(selector => formatNamespacedModelId(selector, t)).join(", "); + return (
{announcement}
-
+
-

{t("sub.featured")}

+

{t("sub.featured")}

{t("sub.rosterCount", { n: chosen.length, max: FEATURED_MAX })}

+ {t("sub.roster.useConcurrentV2Hint")} +
+ )} + + {rosterReachability && rosterReachability.size > 0 && ( +
+ {t("sub.roster.diagnostics")} +

{t("sub.roster.diagnosticsHint")}

+
    + {chosen.map(selector => { + const surface = rosterReachability.get(selector); + if (!surface) return null; + return ( +
  • + {formatNamespacedModelId(selector, t)} + {surfaceLabel(surface, t)} +
  • + ); + })} +
+
+ )} +