Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 29 additions & 21 deletions app/src/components/settings/panels/AIPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -799,24 +799,32 @@ const MetricTile = ({
value: string;
detail?: string;
}) => (
<div className="rounded-md bg-stone-50 dark:bg-neutral-800/60 px-3 py-2">
<div className="text-[10px] font-semibold uppercase tracking-wide text-stone-400 dark:text-neutral-500">
<div className="min-w-0 overflow-hidden rounded-md bg-stone-50 dark:bg-neutral-800/60 px-3 py-2">
<div className="truncate text-[10px] font-semibold uppercase tracking-wide text-stone-400 dark:text-neutral-500">
{label}
</div>
<div className="mt-1 text-sm font-semibold text-stone-900 dark:text-neutral-100">{value}</div>
<div className="mt-1 truncate text-sm font-semibold text-stone-900 dark:text-neutral-100">
{value}
</div>
{detail ? (
<div className="mt-0.5 text-[11px] text-stone-500 dark:text-neutral-400">{detail}</div>
<div className="mt-0.5 truncate text-[11px] text-stone-500 dark:text-neutral-400">
{detail}
</div>
) : null}
</div>
);

const FormulaRow = ({ label, value, detail }: { label: string; value: string; detail: string }) => (
<div className="rounded-md border border-stone-200 dark:border-neutral-800 bg-white dark:bg-neutral-900 px-3 py-2">
<div className="min-w-0 overflow-hidden rounded-md border border-stone-200 dark:border-neutral-800 bg-white dark:bg-neutral-900 px-3 py-2">
<div className="flex items-center justify-between gap-3">
<span className="text-xs font-medium text-stone-800 dark:text-neutral-100">{label}</span>
<span className="font-mono text-xs text-stone-600 dark:text-neutral-300">{value}</span>
<span className="min-w-0 truncate text-xs font-medium text-stone-800 dark:text-neutral-100">
{label}
</span>
<span className="shrink-0 font-mono text-xs text-stone-600 dark:text-neutral-300">
{value}
</span>
</div>
<div className="mt-1 text-[11px] text-stone-500 dark:text-neutral-400">{detail}</div>
<div className="mt-1 truncate text-[11px] text-stone-500 dark:text-neutral-400">{detail}</div>
</div>
);

Expand Down Expand Up @@ -1053,7 +1061,7 @@ const BackgroundLoopControls = ({
</div>
)}

<section className="grid gap-3 lg:grid-cols-[minmax(0,1fr)_minmax(300px,0.8fr)]">
<section className="grid gap-3 md:grid-cols-[minmax(0,1fr)_minmax(260px,0.8fr)]">
<div className="space-y-3">
<div className="rounded-lg border border-stone-200 dark:border-neutral-800 bg-stone-50 dark:bg-neutral-800/60 p-3">
<div className="mb-3 flex items-center justify-between gap-3">
Expand Down Expand Up @@ -1101,9 +1109,9 @@ const BackgroundLoopControls = ({
void applyHeartbeatPatch({ notify_meetings: !settings.notify_meetings })
}
/>
<div className="grid gap-2 rounded-lg border border-stone-200 dark:border-neutral-800 bg-white dark:bg-neutral-900 px-3 py-2 md:grid-cols-3">
<label className="space-y-1 text-xs font-medium text-stone-700 dark:text-neutral-200">
<span>Calendar cap</span>
<div className="grid gap-2 rounded-lg border border-stone-200 dark:border-neutral-800 bg-white dark:bg-neutral-900 px-3 py-2 sm:grid-cols-3">
<label className="min-w-0 space-y-1 text-xs font-medium text-stone-700 dark:text-neutral-200">
<span className="whitespace-nowrap">Calendar cap</span>
<select
Comment on lines +1113 to 1115
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.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Replace hard-coded select labels with i18n keys via useT().

These three changed labels are user-visible literals in JSX and should be translated keys (useT()), with corresponding entries added to locale files.

As per coding guidelines: “Every user-visible string in app/src/** ... must go through useT() ... Hard-coded literals in JSX ... are not allowed. Add the new key to app/src/lib/i18n/en.ts in the same PR.”

Also applies to: 1131-1133, 1149-1151

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/src/components/settings/panels/AIPanel.tsx` around lines 1113 - 1115, The
JSX currently contains hard-coded user-visible strings (e.g., the label
"Calendar cap" and the select option labels around lines in AIPanel.tsx) which
must be replaced with translations via useT(); update the component to call
const t = useT() and replace those literal strings with
t('ai.calendarCap.label') (and analogous keys for the two other selects
referenced at 1131-1133 and 1149-1151), then add corresponding keys and English
values to app/src/lib/i18n/en.ts in this PR so the UI strings are localized.

value={maxCalendarConnectionsPerTick}
disabled={saving === 'max_calendar_connections_per_tick'}
Expand All @@ -1120,8 +1128,8 @@ const BackgroundLoopControls = ({
))}
</select>
</label>
<label className="space-y-1 text-xs font-medium text-stone-700 dark:text-neutral-200">
<span>Meeting lookahead</span>
<label className="min-w-0 space-y-1 text-xs font-medium text-stone-700 dark:text-neutral-200">
<span className="whitespace-nowrap">Meeting lookahead</span>
<select
value={settings.meeting_lookahead_minutes}
disabled={saving === 'meeting_lookahead_minutes'}
Expand All @@ -1138,8 +1146,8 @@ const BackgroundLoopControls = ({
))}
</select>
</label>
<label className="space-y-1 text-xs font-medium text-stone-700 dark:text-neutral-200">
<span>Reminder lookahead</span>
<label className="min-w-0 space-y-1 text-xs font-medium text-stone-700 dark:text-neutral-200">
<span className="whitespace-nowrap">Reminder lookahead</span>
<select
value={settings.reminder_lookahead_minutes}
disabled={saving === 'reminder_lookahead_minutes'}
Expand Down Expand Up @@ -1240,16 +1248,16 @@ const BackgroundLoopControls = ({
<div className="divide-y divide-stone-200 dark:divide-neutral-800">
{loops.map(loop => (
<div key={loop.name} className="grid gap-2 px-3 py-3 md:grid-cols-[150px_1fr]">
<div>
<div className="text-sm font-medium text-stone-900 dark:text-neutral-100">
<div className="min-w-0">
<div className="truncate text-sm font-medium text-stone-900 dark:text-neutral-100">
{loop.name}
</div>
<div className="mt-0.5 flex flex-wrap gap-1 text-[11px] text-stone-500 dark:text-neutral-400">
<span>{loop.enabled ? 'on' : 'off'}</span>
<span>{loop.cadence}</span>
</div>
</div>
<div className="text-xs text-stone-600 dark:text-neutral-300">
<div className="min-w-0 text-xs text-stone-600 dark:text-neutral-300">
<div>{loop.work}</div>
<div className="mt-1 font-mono text-[11px] text-stone-500 dark:text-neutral-400">
route: {loop.route}
Expand All @@ -1262,9 +1270,9 @@ const BackgroundLoopControls = ({
</div>
</div>

<div className="rounded-lg border border-stone-200 dark:border-neutral-800 bg-white dark:bg-neutral-900 p-3">
<div className="min-w-0 overflow-hidden rounded-lg border border-stone-200 dark:border-neutral-800 bg-white dark:bg-neutral-900 p-3">
<div className="flex items-center justify-between gap-3">
<div>
<div className="min-w-0">
<div className="text-sm font-semibold text-stone-900 dark:text-neutral-100">
Recent usage ledger
</div>
Expand Down
2 changes: 1 addition & 1 deletion app/src/lib/i18n/chunks/ar-4.ts
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,7 @@ const ar4: TranslationMap = {
'settings.ai.clearStoredKey': 'مسح المفتاح المحفوظ',
'settings.ai.connectProvider': 'ربط مزود',
'settings.ai.customRouting': 'توجيه مخصص',
'settings.ai.defaultResolvesTo': 'OpenHuman',
'settings.ai.defaultResolvesTo': 'الافتراضي يُحل إلى',
'settings.ai.discard': 'تجاهل',
'settings.ai.editProvider': 'تعديل المزود',
'settings.ai.llmProviders': 'مزودو LLM',
Expand Down
2 changes: 1 addition & 1 deletion app/src/lib/i18n/chunks/bn-4.ts
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,7 @@ const bn4: TranslationMap = {
'settings.ai.clearStoredKey': 'সংরক্ষিত কী মুছুন',
'settings.ai.connectProvider': 'প্রোভাইডার সংযোগ করুন',
'settings.ai.customRouting': 'কাস্টম রুটিং',
'settings.ai.defaultResolvesTo': 'OpenHuman',
'settings.ai.defaultResolvesTo': 'ডিফল্ট এতে সমাধান হয়',
'settings.ai.discard': 'বাতিল করুন',
'settings.ai.editProvider': 'প্রোভাইডার সম্পাদনা করুন',
'settings.ai.llmProviders': 'LLM প্রোভাইডার',
Expand Down
2 changes: 1 addition & 1 deletion app/src/lib/i18n/chunks/de-4.ts
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,7 @@ const de4: TranslationMap = {
'settings.ai.clearStoredKey': 'Gespeicherten Schlüssel löschen',
'settings.ai.connectProvider': 'Anbieter verbinden',
'settings.ai.customRouting': 'Benutzerdefiniertes Routing',
'settings.ai.defaultResolvesTo': 'OpenHuman',
'settings.ai.defaultResolvesTo': 'Standard wird aufgelöst zu',
'settings.ai.discard': 'Verwerfen',
'settings.ai.editProvider': 'Anbieter bearbeiten',
'settings.ai.llmProviders': 'LLM Anbieter',
Expand Down
2 changes: 1 addition & 1 deletion app/src/lib/i18n/chunks/en-4.ts
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,7 @@ const en4: TranslationMap = {
'settings.ai.clearStoredKey': 'Clear stored key',
'settings.ai.connectProvider': 'Connect provider',
'settings.ai.customRouting': 'Custom routing',
'settings.ai.defaultResolvesTo': 'OpenHuman',
'settings.ai.defaultResolvesTo': 'Default resolves to',
'settings.ai.discard': 'Discard',
'settings.ai.editProvider': 'Edit provider',
'settings.ai.llmProviders': 'LLM Providers',
Expand Down
2 changes: 1 addition & 1 deletion app/src/lib/i18n/chunks/es-4.ts
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,7 @@ const es4: TranslationMap = {
'settings.ai.clearStoredKey': 'Borrar clave almacenada',
'settings.ai.connectProvider': 'Conectar proveedor',
'settings.ai.customRouting': 'Enrutamiento personalizado',
'settings.ai.defaultResolvesTo': 'OpenHuman',
'settings.ai.defaultResolvesTo': 'El valor predeterminado se resuelve a',
'settings.ai.discard': 'Descartar',
'settings.ai.editProvider': 'Editar proveedor',
'settings.ai.llmProviders': 'Proveedores LLM',
Expand Down
2 changes: 1 addition & 1 deletion app/src/lib/i18n/chunks/fr-4.ts
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,7 @@ const fr4: TranslationMap = {
'settings.ai.clearStoredKey': 'Effacer la clé stockée',
'settings.ai.connectProvider': 'Connecter un fournisseur',
'settings.ai.customRouting': 'Routage personnalisé',
'settings.ai.defaultResolvesTo': 'OpenHuman',
'settings.ai.defaultResolvesTo': 'Par défaut résolu en',
'settings.ai.discard': 'Annuler',
'settings.ai.editProvider': 'Modifier le fournisseur',
'settings.ai.llmProviders': 'Fournisseurs LLM',
Expand Down
2 changes: 1 addition & 1 deletion app/src/lib/i18n/chunks/hi-4.ts
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,7 @@ const hi4: TranslationMap = {
'settings.ai.clearStoredKey': 'स्टोर्ड key क्लियर करें',
'settings.ai.connectProvider': 'प्रदाता कनेक्ट करें',
'settings.ai.customRouting': 'कस्टम रूटिंग',
'settings.ai.defaultResolvesTo': 'OpenHuman',
'settings.ai.defaultResolvesTo': 'डिफ़ॉल्ट इसमें हल होता है',
'settings.ai.discard': 'रद्द करें',
'settings.ai.editProvider': 'प्रदाता संपादित करें',
'settings.ai.llmProviders': 'LLM प्रोवाइडर',
Expand Down
2 changes: 1 addition & 1 deletion app/src/lib/i18n/chunks/id-4.ts
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,7 @@ const id4: TranslationMap = {
'settings.ai.clearStoredKey': 'Hapus key tersimpan',
'settings.ai.connectProvider': 'Hubungkan penyedia',
'settings.ai.customRouting': 'Routing kustom',
'settings.ai.defaultResolvesTo': 'OpenHuman',
'settings.ai.defaultResolvesTo': 'Default diarahkan ke',
'settings.ai.discard': 'Buang',
'settings.ai.editProvider': 'Edit penyedia',
'settings.ai.llmProviders': 'Penyedia LLM',
Expand Down
2 changes: 1 addition & 1 deletion app/src/lib/i18n/chunks/it-4.ts
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,7 @@ const it4: TranslationMap = {
'settings.ai.clearStoredKey': 'Cancella chiave memorizzata',
'settings.ai.connectProvider': 'Connetti provider',
'settings.ai.customRouting': 'Instradamento personalizzato',
'settings.ai.defaultResolvesTo': 'OpenHuman',
'settings.ai.defaultResolvesTo': 'Il valore predefinito si risolve in',
'settings.ai.discard': 'Scarta',
'settings.ai.editProvider': 'Modifica provider',
'settings.ai.llmProviders': 'Provider LLM',
Expand Down
2 changes: 1 addition & 1 deletion app/src/lib/i18n/chunks/ko-4.ts
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,7 @@ const ko4: TranslationMap = {
'settings.ai.clearStoredKey': '저장된 키 지우기',
'settings.ai.connectProvider': '제공업체 연결',
'settings.ai.customRouting': '사용자 지정 라우팅',
'settings.ai.defaultResolvesTo': 'OpenHuman',
'settings.ai.defaultResolvesTo': '기본값은 다음으로 확인됨',
'settings.ai.discard': '취소',
'settings.ai.editProvider': '제공업체 편집',
'settings.ai.llmProviders': 'LLM 제공업체',
Expand Down
2 changes: 1 addition & 1 deletion app/src/lib/i18n/chunks/pt-4.ts
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,7 @@ const pt4: TranslationMap = {
'settings.ai.clearStoredKey': 'Limpar chave armazenada',
'settings.ai.connectProvider': 'Conectar provedor',
'settings.ai.customRouting': 'Roteamento personalizado',
'settings.ai.defaultResolvesTo': 'OpenHuman',
'settings.ai.defaultResolvesTo': 'O padrão resolve para',
'settings.ai.discard': 'Descartar',
'settings.ai.editProvider': 'Editar provedor',
'settings.ai.llmProviders': 'Provedores LLM',
Expand Down
2 changes: 1 addition & 1 deletion app/src/lib/i18n/chunks/ru-4.ts
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,7 @@ const ru4: TranslationMap = {
'settings.ai.clearStoredKey': 'Очистить сохранённый ключ',
'settings.ai.connectProvider': 'Подключить провайдера',
'settings.ai.customRouting': 'Пользовательская маршрутизация',
'settings.ai.defaultResolvesTo': 'OpenHuman',
'settings.ai.defaultResolvesTo': 'По умолчанию используется',
'settings.ai.discard': 'Отменить',
'settings.ai.editProvider': 'Изменить провайдера',
'settings.ai.llmProviders': 'LLM-провайдеры',
Expand Down
2 changes: 1 addition & 1 deletion app/src/lib/i18n/chunks/zh-CN-4.ts
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,7 @@ const zhCN4: TranslationMap = {
'settings.ai.clearStoredKey': '清除已存储的密钥',
'settings.ai.connectProvider': '连接 {label}',
'settings.ai.customRouting': '自定义路由',
'settings.ai.defaultResolvesTo': 'OpenHuman',
'settings.ai.defaultResolvesTo': '默认解析为',
'settings.ai.discard': '放弃',
'settings.ai.editProvider': '编辑提供商',
'settings.ai.llmProviders': 'LLM 提供商',
Expand Down
2 changes: 1 addition & 1 deletion app/src/lib/i18n/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1646,7 +1646,7 @@ const en: TranslationMap = {
'settings.ai.clearStoredKey': 'Clear stored key',
'settings.ai.connectProvider': 'Connect',
'settings.ai.customRouting': 'Custom routing',
'settings.ai.defaultResolvesTo': 'OpenHuman',
'settings.ai.defaultResolvesTo': 'Default resolves to',
'settings.ai.discard': 'Discard',
'settings.ai.editProvider': 'Edit {label}',
'settings.ai.llmProviders': 'LLM Providers',
Expand Down
2 changes: 1 addition & 1 deletion app/src/lib/i18n/ko.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1565,7 +1565,7 @@ const ko: TranslationMap = {
'settings.ai.clearStoredKey': '저장된 키 지우기',
'settings.ai.connectProvider': '연결',
'settings.ai.customRouting': '사용자 지정 라우팅',
'settings.ai.defaultResolvesTo': 'OpenHuman',
'settings.ai.defaultResolvesTo': '기본값은 다음으로 확인됨',
'settings.ai.discard': '취소',
'settings.ai.editProvider': '{label} 편집',
'settings.ai.llmProviders': 'LLM 제공업체',
Expand Down
2 changes: 1 addition & 1 deletion app/src/pages/Settings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ const VoiceIcon = (
const WrappedSettingsPage = ({ children }: { children: ReactNode }) => {
return (
<div className="p-4 pt-6">
<div className="max-w-lg mx-auto bg-white dark:bg-neutral-900 rounded-2xl shadow-soft border border-stone-200 dark:border-neutral-800 overflow-hidden">
<div className="max-w-3xl mx-auto bg-white dark:bg-neutral-900 rounded-2xl shadow-soft border border-stone-200 dark:border-neutral-800 overflow-hidden">
{children}
</div>
</div>
Expand Down
2 changes: 2 additions & 0 deletions src/openhuman/agent/agents/integrations_agent/prompt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,7 @@ mod tests {
tools: Vec::new(),
gated_tools: Vec::new(),
connected: true,
non_active_status: None,
}];
let body = build(&ctx_with(&integrations, &[])).unwrap();
assert!(body.contains("## Connected Integrations"));
Expand Down Expand Up @@ -275,6 +276,7 @@ mod tests {
tools: Vec::new(),
gated_tools: Vec::new(),
connected: false,
non_active_status: None,
}];
let body = build(&ctx_with(&integrations, &[])).unwrap();
assert!(!body.contains("## Connected Integrations"));
Expand Down
5 changes: 5 additions & 0 deletions src/openhuman/agent/agents/orchestrator/prompt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,7 @@ mod tests {
tools: Vec::new(),
gated_tools: Vec::new(),
connected: true,
non_active_status: None,
}];
let body = build(&ctx_with(&integrations)).unwrap();
assert!(body.contains("## Connected Integrations"));
Expand Down Expand Up @@ -268,6 +269,7 @@ mod tests {
tools: Vec::new(),
gated_tools: Vec::new(),
connected: true,
non_active_status: None,
}];
let body = build(&ctx_with(&integrations)).unwrap();
assert!(body.contains("## Connected Integrations"));
Expand All @@ -290,13 +292,15 @@ mod tests {
tools: Vec::new(),
gated_tools: Vec::new(),
connected: true,
non_active_status: None,
},
ConnectedIntegration {
toolkit: "linear".into(),
description: "Tracker.".into(),
tools: Vec::new(),
gated_tools: Vec::new(),
connected: false,
non_active_status: None,
},
];
let body = build(&ctx_with(&integrations)).unwrap();
Expand All @@ -312,6 +316,7 @@ mod tests {
tools: Vec::new(),
gated_tools: Vec::new(),
connected: false,
non_active_status: None,
}];
let body = build(&ctx_with(&integrations)).unwrap();
assert!(!body.contains("## Connected Integrations"));
Expand Down
2 changes: 2 additions & 0 deletions src/openhuman/agent/agents/welcome/prompt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -181,13 +181,15 @@ mod tests {
tools: Vec::new(),
gated_tools: Vec::new(),
connected: true,
non_active_status: None,
},
ConnectedIntegration {
toolkit: "notion".into(),
description: "Pitch during onboarding.".into(),
tools: Vec::new(),
gated_tools: Vec::new(),
connected: false,
non_active_status: None,
},
];
let body = build(&ctx_with(&integrations)).unwrap();
Expand Down
4 changes: 4 additions & 0 deletions src/openhuman/agent/harness/subagent_runner/ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -689,6 +689,10 @@ async fn run_typed_mode(
// the user pref and doesn't change per-spawn.
gated_tools: cached_integration.gated_tools.clone(),
connected: cached_integration.connected,
// Inherit the cached non-active status — this spawn
// path only fires on connected toolkits, but keep the
// field consistent with the source row for #2365.
non_active_status: cached_integration.non_active_status.clone(),
};
let integration = &integration;
// Fuzzy-filter the toolkit's actions against the task prompt
Expand Down
1 change: 1 addition & 0 deletions src/openhuman/agent/harness/test_support_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1564,6 +1564,7 @@ async fn orchestrator_prompt_drives_composio_call_via_delegation_chain() {
tools: Vec::new(),
gated_tools: Vec::new(),
connected: true,
non_active_status: None,
}];
let ctx = {
use crate::openhuman::context::prompt::{LearnedContextData, ToolCallFormat};
Expand Down
13 changes: 13 additions & 0 deletions src/openhuman/agent/prompts/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,19 @@ pub struct ConnectedIntegration {
/// and the orchestrator must point the user at Settings instead of
/// attempting to delegate.
pub connected: bool,
/// Raw upstream connection status when a connection row exists but
/// is not `ACTIVE` — e.g. `"INITIATED"`, `"INITIALIZING"`,
/// `"FAILED"`, `"EXPIRED"`. `None` means either the user is
/// `ACTIVE` (use `connected = true`) OR there is no connection
/// row at all (truly disconnected).
///
/// Used by the `integrations_agent` spawn-gate to surface the
/// real reason a delegation can't proceed — see issue #2365
/// ("Agent says Gmail is disconnected when sending email"). The
/// gate previously emitted the same "not authorized yet" message
/// regardless of whether OAuth was mid-flight, the token had
/// expired, or the user had simply never started the flow.
pub non_active_status: Option<String>,
}

/// A toolkit action that exists in the catalog but is currently hidden from
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

[major] This field and the related logic in composio/ops.rs and spawn_subagent.rs address issue #2365 — but the PR description says this is a CSS-only/i18n change with no logic changes and marks tests as N/A. Update the description, checklist, and Related section before marking ready.

Expand Down
Loading