From 5e4016702bda8e4b830f29bcba7913858877a649 Mon Sep 17 00:00:00 2001 From: bitmvk Date: Wed, 11 Nov 2026 01:03:53 +0530 Subject: [PATCH 1/3] fix(mobile): subagents on a switched-off environment no longer open a new thread Co-Authored-By: Claude Opus 5.5 --- .../features/threads/ThreadAgentsSheet.tsx | 4 +-- .../threads/thread-subagent-group.tsx | 6 ++--- .../threads/threadAgentsPresentation.test.ts | 25 +++++++++++++++++++ .../threads/threadAgentsPresentation.ts | 8 +++--- .../chat/ThreadRelationshipsControl.tsx | 17 +++++++------ 5 files changed, 42 insertions(+), 18 deletions(-) diff --git a/apps/mobile/src/features/threads/ThreadAgentsSheet.tsx b/apps/mobile/src/features/threads/ThreadAgentsSheet.tsx index e78239955e4af..0ff64c719dd5d 100644 --- a/apps/mobile/src/features/threads/ThreadAgentsSheet.tsx +++ b/apps/mobile/src/features/threads/ThreadAgentsSheet.tsx @@ -141,9 +141,7 @@ function AgentRow(props: { }) { const { subagent } = props; const { presentationById } = useEnvironments(); - const target = subagentThreadTarget(subagent, props.environmentId, (id) => - presentationById.has(id), - ); + const target = subagentThreadTarget(subagent, props.environmentId, presentationById); const row = ( diff --git a/apps/mobile/src/features/threads/thread-subagent-group.tsx b/apps/mobile/src/features/threads/thread-subagent-group.tsx index f3d3f3b5252b6..2a1b0dfd5d806 100644 --- a/apps/mobile/src/features/threads/thread-subagent-group.tsx +++ b/apps/mobile/src/features/threads/thread-subagent-group.tsx @@ -189,10 +189,8 @@ export function ThreadSubagentGroup(props: { {!grouped || expanded ? ( {agents.map((agent) => { - // A task in a linked environment opens there, if this app is connected to it. - const target = subagentThreadTarget(agent, props.environmentId, (id) => - presentationById.has(id), - ); + // A task in a linked environment opens there, if it is enabled in this app. + const target = subagentThreadTarget(agent, props.environmentId, presentationById); return ( { ).toBeNull(); }); }); + +describe("subagentThreadTarget", () => { + const here = EnvironmentId.make("env-here"); + const laptop = EnvironmentId.make("env-laptop"); + const remote = { + childThreadId: null, + remoteChild: { + environmentId: laptop, + threadId: ThreadId.make("remote-thread"), + label: "Laptop", + }, + }; + + it("opens a remote task only in an enabled saved environment", () => { + expect( + subagentThreadTarget(remote, here, new Map([[laptop, { entry: { enabled: true } }]])), + ).toEqual({ environmentId: laptop, threadId: "remote-thread" }); + expect( + subagentThreadTarget(remote, here, new Map([[laptop, { entry: { enabled: false } }]])), + ).toBeNull(); + expect(subagentThreadTarget(remote, here, new Map())).toBeNull(); + }); +}); diff --git a/apps/mobile/src/features/threads/threadAgentsPresentation.ts b/apps/mobile/src/features/threads/threadAgentsPresentation.ts index 7f560497da254..cddf59d5264d7 100644 --- a/apps/mobile/src/features/threads/threadAgentsPresentation.ts +++ b/apps/mobile/src/features/threads/threadAgentsPresentation.ts @@ -36,19 +36,19 @@ export interface SubagentRowPresentation { /** * Where an agent's thread opens: here, or in the linked environment that runs - * it. Null when there is no thread, or this app is not connected to that - * environment. + * it. Null when there is no thread, or that environment is not saved and + * enabled in this app. */ export function subagentThreadTarget( subagent: Pick, environmentId: EnvironmentId, - isKnownEnvironment: (environmentId: EnvironmentId) => boolean, + environments: ReadonlyMap, ): { readonly environmentId: EnvironmentId; readonly threadId: ThreadId } | null { if (subagent.childThreadId !== null) { return { environmentId, threadId: subagent.childThreadId }; } const remote = subagent.remoteChild; - return remote !== undefined && isKnownEnvironment(remote.environmentId) + return remote !== undefined && environments.get(remote.environmentId)?.entry.enabled === true ? { environmentId: remote.environmentId, threadId: remote.threadId } : null; } diff --git a/apps/web/src/components/chat/ThreadRelationshipsControl.tsx b/apps/web/src/components/chat/ThreadRelationshipsControl.tsx index 102d9734ded92..b2fc3e101e3a6 100644 --- a/apps/web/src/components/chat/ThreadRelationshipsControl.tsx +++ b/apps/web/src/components/chat/ThreadRelationshipsControl.tsx @@ -266,6 +266,8 @@ function RemoteSubagentRow(props: { }) { const navigate = useNavigate(); const environment = useEnvironment(props.agent.remoteChild.environmentId); + // A saved environment that is switched off can't open its threads. + const canOpen = environment?.entry.enabled === true; const { remoteChild } = props.agent; const status = props.agent.status; const failed = status === "failed"; @@ -283,9 +285,9 @@ function RemoteSubagentRow(props: { size="sm" variant="ghost" part="row" - aria-disabled={environment === null || undefined} + aria-disabled={!canOpen || undefined} onClick={() => { - if (environment === null) return; + if (!canOpen) return; void navigate({ to: "/$environmentId/$threadId", params: buildThreadRouteParams( @@ -319,7 +321,7 @@ function RemoteSubagentRow(props: { { - if (environment === null) return; + if (!canOpen) return; void navigate({ to: "/$environmentId/$threadId", params: buildThreadRouteParams(parentRef), @@ -383,9 +386,9 @@ function DelegatedFromRow(props: { From 2b07e9a58dc932d1d3f003aacb54257422d9e6dd Mon Sep 17 00:00:00 2001 From: bitmvk Date: Wed, 11 Nov 2026 01:31:43 +0530 Subject: [PATCH 2/3] fix: hint to turn on a saved environment instead of connecting to it Co-Authored-By: Claude Opus 5.5 --- .../src/features/threads/ThreadAgentsSheet.tsx | 4 +++- .../chat/ThreadRelationshipsControl.tsx | 16 ++++++++++------ 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/apps/mobile/src/features/threads/ThreadAgentsSheet.tsx b/apps/mobile/src/features/threads/ThreadAgentsSheet.tsx index 0ff64c719dd5d..f21cde9deff4c 100644 --- a/apps/mobile/src/features/threads/ThreadAgentsSheet.tsx +++ b/apps/mobile/src/features/threads/ThreadAgentsSheet.tsx @@ -162,7 +162,9 @@ function AgentRow(props: { accessibilityHint={ subagent.remoteChild === undefined ? "Provider-managed agent. Its work appears in the transcript." - : `Runs on ${subagent.remoteChild.label}. Connect to it to open its thread.` + : presentationById.has(subagent.remoteChild.environmentId) + ? `Runs on ${subagent.remoteChild.label}. Turn it on in Settings to open its thread.` + : `Runs on ${subagent.remoteChild.label}. Connect to it to open its thread.` } > {row} diff --git a/apps/web/src/components/chat/ThreadRelationshipsControl.tsx b/apps/web/src/components/chat/ThreadRelationshipsControl.tsx index b2fc3e101e3a6..4cea5b617f2b7 100644 --- a/apps/web/src/components/chat/ThreadRelationshipsControl.tsx +++ b/apps/web/src/components/chat/ThreadRelationshipsControl.tsx @@ -321,9 +321,11 @@ function RemoteSubagentRow(props: { @@ -386,9 +388,11 @@ function DelegatedFromRow(props: { From 0980421291a3671a7e83109456423360cac70115 Mon Sep 17 00:00:00 2001 From: Julius Marminge Date: Sun, 15 Nov 2026 06:05:31 +0000 Subject: [PATCH 3/3] fix(web): transcript subagent links and parent breadcrumb need an enabled environment Co-Authored-By: Claude Opus 5.5 --- apps/web/src/components/ChatView.tsx | 4 ++-- apps/web/src/components/chat/V2LifecycleRow.tsx | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/web/src/components/ChatView.tsx b/apps/web/src/components/ChatView.tsx index 9c015a74b9b9b..0155cd1ad2382 100644 --- a/apps/web/src/components/ChatView.tsx +++ b/apps/web/src/components/ChatView.tsx @@ -938,8 +938,8 @@ export default function ChatView(props: ChatViewProps) { return { title: delegatedFromThread?.title ?? delegatedFrom.title, environmentLabel: delegatedFromEnvironment?.label ?? activeThread?.linkOrigin?.label ?? "", - // Only an environment this client is connected to can open the parent. - ...(delegatedFromEnvironment === null + // Only an environment enabled in this client can open the parent. + ...(delegatedFromEnvironment?.entry.enabled !== true ? {} : { open: (): void => { diff --git a/apps/web/src/components/chat/V2LifecycleRow.tsx b/apps/web/src/components/chat/V2LifecycleRow.tsx index 920586f6d1dbb..085b906ebe6f2 100644 --- a/apps/web/src/components/chat/V2LifecycleRow.tsx +++ b/apps/web/src/components/chat/V2LifecycleRow.tsx @@ -423,11 +423,11 @@ function SubagentTimelineLink(props: { const remoteEnvironment = useEnvironment(remote?.environmentId ?? null); const navigate = useNavigate(); const threadId = props.threadId; - // A task in a linked environment opens there, if this client is connected to it. + // A task in a linked environment opens there, if it is enabled in this client. const open = threadId !== null ? () => props.onOpenThread(threadId) - : remote !== undefined && remoteEnvironment !== null + : remote !== undefined && remoteEnvironment?.entry.enabled === true ? () => void navigate({ to: "/$environmentId/$threadId",