fix: resolve chat prompt read-only state for subagent sessions

Allows prompting subagent sessions without needing the parent record
Keeps subagents read-only when prompting is disabled
Covers root and subagent cases with unit tests
This commit is contained in:
Bohdan Triapitsyn
2026-07-28 02:50:02 +03:00
parent 3fa0c5be57
commit 5787ea5d49
3 changed files with 47 additions and 1 deletions
@@ -43,6 +43,7 @@ import {
useScopedBlockingPermissions,
useScopedBlockingQuestions,
useParentSession,
useSession,
} from '@/sync/sync-context';
import { useSync } from '@/sync/use-sync';
import { usePlanDetection } from '@/hooks/usePlanDetection';
@@ -53,6 +54,7 @@ import { getEmbeddedSessionChatOriginSessionId } from '@/components/layout/conte
import { isFullySyntheticMessage } from '@/lib/messages/synthetic';
import { normalizeUserDisplayParts } from './message/normalizeUserDisplayParts';
import { findShellCommandForMessage, isUserShellMarkerMessage } from './lib/shellBridge';
import { resolveChatPromptReadOnly } from './chatPromptReadOnly';
const EMPTY_MESSAGES: Array<{ info: Message; parts: Part[] }> = [];
const IDLE_SESSION_STATUS = { type: 'idle' as const };
@@ -679,6 +681,7 @@ export const ChatContainer: React.FC<ChatContainerProps> = ({ active = true, aut
const isDesktopExpandedInput = isExpandedInput;
const useCompactDraftLayout = isMobile || isVSCode || chatSurfaceMode === 'mini-chat';
const messageListRef = React.useRef<MessageListHandle | null>(null);
const currentSession = useSession(currentSessionId, effectiveSessionDirectory);
const parentSession = useParentSession(currentSessionId, effectiveSessionDirectory);
// In the embedded session-chat iframe, hide "Return to parent" when
@@ -712,7 +715,7 @@ export const ChatContainer: React.FC<ChatContainerProps> = ({ active = true, aut
{t('chat.container.returnToParent.label')}
</Button>
) : null;
const promptReadOnly = parentSession ? !allowPromptingSubagentSessions : readOnly;
const promptReadOnly = resolveChatPromptReadOnly(currentSession, allowPromptingSubagentSessions, readOnly);
React.useEffect(() => {
// VS Code/Cursor/Positron webviews delete window.parent (and window.top).
@@ -0,0 +1,30 @@
import { describe, expect, test } from 'bun:test';
import type { Session } from '@opencode-ai/sdk/v2';
import { resolveChatPromptReadOnly } from './chatPromptReadOnly';
const session = (parentID?: string): Session => ({
id: 'session',
slug: 'session',
title: 'Session',
version: '1',
projectID: 'project',
directory: '/repo',
parentID,
time: { created: 1, updated: 1 },
});
describe('resolveChatPromptReadOnly', () => {
test('allows prompting a subagent without requiring its parent record', () => {
expect(resolveChatPromptReadOnly(session('parent'), true, true)).toBe(false);
});
test('keeps a subagent read-only when prompting is disabled', () => {
expect(resolveChatPromptReadOnly(session('parent'), false, false)).toBe(true);
});
test('preserves the surface read-only state for root sessions', () => {
expect(resolveChatPromptReadOnly(session(), true, true)).toBe(true);
expect(resolveChatPromptReadOnly(session(), true, false)).toBe(false);
});
});
@@ -0,0 +1,13 @@
import type { Session } from '@opencode-ai/sdk/v2';
export const resolveChatPromptReadOnly = (
session: Session | null | undefined,
allowPromptingSubagentSessions: boolean,
readOnly: boolean,
): boolean => {
if (session?.parentID) {
return !allowPromptingSubagentSessions;
}
return readOnly;
};