mirror of
https://github.com/Kilo-Org/kilocode.git
synced 2026-08-28 19:11:03 +08:00
fix(vscode): hide subagent promotion in readonly views
This commit is contained in:
@@ -163,6 +163,7 @@ export interface MessagePartProps {
|
||||
working?: boolean
|
||||
feedback?: MessageFeedbackControls
|
||||
throughput?: JSX.Element
|
||||
readonly?: boolean
|
||||
}
|
||||
|
||||
export type PartComponent = Component<MessagePartProps>
|
||||
@@ -1044,6 +1045,7 @@ export function Part(props: MessagePartProps) {
|
||||
working={props.working}
|
||||
feedback={props.feedback}
|
||||
throughput={props.throughput}
|
||||
readonly={props.readonly}
|
||||
/>
|
||||
</Show>
|
||||
)
|
||||
@@ -1068,6 +1070,7 @@ export interface ToolProps {
|
||||
locked?: boolean
|
||||
animate?: boolean
|
||||
reveal?: boolean
|
||||
readonly?: boolean
|
||||
}
|
||||
|
||||
export type ToolComponent = Component<ToolProps>
|
||||
@@ -1267,6 +1270,7 @@ PART_MAPPING["tool"] = function ToolPartDisplay(props) {
|
||||
forceOpen={props.forceOpen}
|
||||
animate
|
||||
reveal={props.animate}
|
||||
readonly={props.readonly}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -1344,6 +1348,7 @@ PART_MAPPING["tool"] = function ToolPartDisplay(props) {
|
||||
forceOpenFile={props.forceOpenFile}
|
||||
animate
|
||||
reveal={props.animate}
|
||||
readonly={props.readonly}
|
||||
/>
|
||||
</ToolApprovalProvider>
|
||||
</Match>
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
backgroundJobAgents,
|
||||
showBackgroundAgent,
|
||||
} from "../../webview-ui/src/components/chat/background-agents"
|
||||
import { childForeground, showChildPromotion } from "../../webview-ui/src/components/chat/task-tool-state"
|
||||
import type {
|
||||
BackgroundJobInfo,
|
||||
PermissionRequest,
|
||||
@@ -73,6 +74,22 @@ describe("backgroundAgents", () => {
|
||||
expect(backgroundAgents([taskPart({ child: "ses_child" })], { ses_child: busy })).toEqual([])
|
||||
})
|
||||
|
||||
it("identifies each parallel foreground child independently", () => {
|
||||
const status = { ses_a: busy, ses_b: busy }
|
||||
|
||||
expect(childForeground("ses_a", {}, {}, status)).toBe(true)
|
||||
expect(childForeground("ses_b", {}, {}, status)).toBe(true)
|
||||
expect(childForeground("ses_a", { background: true }, {}, status)).toBe(false)
|
||||
expect(childForeground("ses_b", {}, { background: true }, status)).toBe(false)
|
||||
expect(childForeground("ses_a", {}, {}, { ses_a: idle })).toBe(false)
|
||||
expect(childForeground("ses_a", {}, {}, { ses_a: { type: "retry", attempt: 1, message: "retry", next: 1 } })).toBe(
|
||||
true,
|
||||
)
|
||||
expect(childForeground(undefined, {}, {}, status)).toBe(false)
|
||||
expect(showChildPromotion("ses_a", {}, {}, status, false)).toBe(true)
|
||||
expect(showChildPromotion("ses_a", {}, {}, status, true)).toBe(false)
|
||||
})
|
||||
|
||||
it("ignores agents whose session is no longer working", () => {
|
||||
const tools = [taskPart({ child: "ses_child", background: true })]
|
||||
|
||||
|
||||
@@ -114,6 +114,7 @@ interface AssistantMessageProps {
|
||||
forceOpenFile?: string
|
||||
/** Part behind the currently hovered/focused task-timeline bar, if any. */
|
||||
highlight?: () => TimelineHighlight | undefined
|
||||
readonly?: boolean
|
||||
}
|
||||
|
||||
type ToolStateProps = {
|
||||
@@ -339,6 +340,7 @@ export const AssistantMessage: Component<AssistantMessageProps> = (props) => {
|
||||
reasoningAutoCollapse={display.reasoningAutoCollapse()}
|
||||
feedback={props.feedback}
|
||||
throughput={throughputEl()}
|
||||
readonly={props.readonly}
|
||||
animate={
|
||||
part.type === "tool" &&
|
||||
((part as unknown as ToolPart).state?.status === "pending" ||
|
||||
|
||||
@@ -1332,6 +1332,7 @@ export const MessageList: Component<MessageListProps> = (props) => {
|
||||
activeSearch={activeKey() === row.key}
|
||||
activeSearchPartID={activeKey() === row.key ? activeMatch()?.partId : undefined}
|
||||
activeSearchPartFile={activeKey() === row.key ? activeMatch()?.partFile : undefined}
|
||||
readonly={props.readonly}
|
||||
/>
|
||||
)}
|
||||
</Virtualizer>
|
||||
@@ -1345,6 +1346,7 @@ export const MessageList: Component<MessageListProps> = (props) => {
|
||||
activeSearch={activeKey() === key}
|
||||
activeSearchPartID={activeKey() === key ? activeMatch()?.partId : undefined}
|
||||
activeSearchPartFile={activeKey() === key ? activeMatch()?.partFile : undefined}
|
||||
readonly={props.readonly}
|
||||
/>
|
||||
)}
|
||||
</For>
|
||||
@@ -1360,6 +1362,7 @@ export const MessageList: Component<MessageListProps> = (props) => {
|
||||
activeSearch={activeKey() === row.key}
|
||||
activeSearchPartID={activeKey() === row.key ? activeMatch()?.partId : undefined}
|
||||
activeSearchPartFile={activeKey() === row.key ? activeMatch()?.partFile : undefined}
|
||||
readonly={props.readonly}
|
||||
/>
|
||||
)}
|
||||
</For>
|
||||
|
||||
@@ -21,7 +21,7 @@ import { useVSCode } from "../../context/vscode"
|
||||
import { useWorktreeMode } from "../../context/worktree-mode"
|
||||
import { childID } from "../../context/session-utils"
|
||||
import { openSubagent } from "./open-subagent"
|
||||
import { taskResult, taskRunning, taskVisible } from "./task-tool-state"
|
||||
import { showChildPromotion, taskResult, taskRunning, taskVisible } from "./task-tool-state"
|
||||
|
||||
const TaskToolRenderer: Component<ToolProps> = (props) => {
|
||||
const i18n = useI18n()
|
||||
@@ -38,15 +38,15 @@ const TaskToolRenderer: Component<ToolProps> = (props) => {
|
||||
state: { metadata: props.metadata as { sessionId?: string } },
|
||||
})
|
||||
|
||||
const childForeground = createMemo(() => {
|
||||
const id = childSessionId()
|
||||
if (!id) return false
|
||||
const part = props.partMetadata as { background?: boolean } | undefined
|
||||
const state = props.metadata as { background?: boolean } | undefined
|
||||
if (part?.background === true || state?.background === true) return false
|
||||
const status = session.allStatusMap()[id]
|
||||
return status?.type === "busy" || status?.type === "retry"
|
||||
})
|
||||
const promotable = createMemo(() =>
|
||||
showChildPromotion(
|
||||
childSessionId(),
|
||||
props.partMetadata as Record<string, unknown> | undefined,
|
||||
props.metadata as Record<string, unknown> | undefined,
|
||||
session.allStatusMap(),
|
||||
props.readonly,
|
||||
),
|
||||
)
|
||||
|
||||
const running = createMemo(() => taskRunning(props.status))
|
||||
// BasicTool's forceOpen effect only fires onOpenChange on a false->true
|
||||
@@ -167,7 +167,7 @@ const TaskToolRenderer: Component<ToolProps> = (props) => {
|
||||
</Show>
|
||||
</div>
|
||||
<Show when={childSessionId()}>
|
||||
<Show when={childForeground()}>
|
||||
<Show when={promotable()}>
|
||||
<IconButton
|
||||
icon="play"
|
||||
size="small"
|
||||
|
||||
@@ -26,6 +26,7 @@ interface TranscriptRowViewProps {
|
||||
activeSearchPartID?: string
|
||||
/** For a multi-file apply_patch match, the specific file within that part. */
|
||||
activeSearchPartFile?: string
|
||||
readonly?: boolean
|
||||
}
|
||||
|
||||
export const TranscriptRowView: Component<TranscriptRowViewProps> = (props) => {
|
||||
@@ -92,6 +93,7 @@ export const TranscriptRowView: Component<TranscriptRowViewProps> = (props) => {
|
||||
forceOpenPartID={props.activeSearchPartID}
|
||||
forceOpenFile={props.activeSearchPartFile}
|
||||
highlight={props.highlight}
|
||||
readonly={props.readonly}
|
||||
feedback={{
|
||||
enabled: feedback.telemetryEnabled(),
|
||||
rating: feedback.getRating(row().message.id),
|
||||
|
||||
@@ -1,7 +1,30 @@
|
||||
import type { SessionStatusInfo } from "../../types/messages"
|
||||
|
||||
export function taskRunning(status: string | undefined) {
|
||||
return status === "pending" || status === "running"
|
||||
}
|
||||
|
||||
export function childForeground(
|
||||
id: string | undefined,
|
||||
part: Record<string, unknown> | undefined,
|
||||
state: Record<string, unknown> | undefined,
|
||||
status: Record<string, SessionStatusInfo>,
|
||||
) {
|
||||
if (!id) return false
|
||||
if (part?.background === true || state?.background === true) return false
|
||||
return status[id]?.type === "busy" || status[id]?.type === "retry"
|
||||
}
|
||||
|
||||
export function showChildPromotion(
|
||||
id: string | undefined,
|
||||
part: Record<string, unknown> | undefined,
|
||||
state: Record<string, unknown> | undefined,
|
||||
status: Record<string, SessionStatusInfo>,
|
||||
readonly: boolean | undefined,
|
||||
) {
|
||||
return !readonly && childForeground(id, part, state, status)
|
||||
}
|
||||
|
||||
export function taskVisible(open: boolean | undefined, id: string | undefined) {
|
||||
return open ? id : undefined
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user