feat(chat): align command, shell, and subtask UX (#444)
* feat: reload interface after skills operations - Adds configurable delay before interface reload after skills changes - Introduces polling to wait for application health after reload - Updates UI to show reload message when installing or modifying skills * feat: distinguish skills from commands in UI - Displays skill badge for commands that are registered skills - Prevents editing skills through command management interface - Triggers interface reload after skill operations to reflect changes * feat(chat): align command and subtask UX with opencode parity Route commands/shell via parity paths, render delegated subtasks cleanly, and surface child-session permission/question prompts in parent chat. * fix(ProjectEditDialog): improve layout consistency * fix: update task icon and session handling in ToolPart * feat(chat): add shell-mode input and collapse shell bridge output Switch leading ! to shell mode UX and fold synthetic shell bridge assistant messages into the user shell bubble with inline output actions. * fix: remove AI agent icon from file mention autocomplete * fix: remove unused icon import from file mention component
This commit is contained in:
committed by
GitHub
parent
e4a2486312
commit
85f21cb945
@@ -793,6 +793,28 @@ export const useEventStream = () => {
|
||||
break;
|
||||
}
|
||||
|
||||
const shouldKeepSyntheticUserText = (value: unknown): boolean => {
|
||||
const text = typeof value === 'string' ? value.trim() : '';
|
||||
if (!text) return false;
|
||||
return (
|
||||
text.startsWith('User has requested to enter plan mode') ||
|
||||
text.startsWith('The plan at ') ||
|
||||
text.startsWith('The following tool was executed by the user')
|
||||
);
|
||||
};
|
||||
|
||||
const inferUserRoleFromPart = (): boolean => {
|
||||
const partType = typeof partExt.type === 'string' ? partExt.type : '';
|
||||
if (partType === 'subtask' || partType === 'agent' || partType === 'file') {
|
||||
return true;
|
||||
}
|
||||
if (partType === 'text' && partExt.synthetic === true) {
|
||||
const text = (partExt as { text?: unknown }).text;
|
||||
return shouldKeepSyntheticUserText(text);
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
let roleInfo = 'assistant';
|
||||
if (messageInfo && typeof (messageInfo as { role?: unknown }).role === 'string') {
|
||||
roleInfo = (messageInfo as { role?: string }).role as string;
|
||||
@@ -806,11 +828,18 @@ export const useEventStream = () => {
|
||||
}
|
||||
}
|
||||
|
||||
if (roleInfo !== 'user' && inferUserRoleFromPart()) {
|
||||
roleInfo = 'user';
|
||||
}
|
||||
|
||||
trackMessage(messageId, 'part_received', { role: roleInfo });
|
||||
|
||||
if (roleInfo === 'user' && partExt.synthetic === true) {
|
||||
trackMessage(messageId, 'skipped_synthetic_user_part');
|
||||
break;
|
||||
const text = (partExt as { text?: unknown }).text;
|
||||
if (!shouldKeepSyntheticUserText(text)) {
|
||||
trackMessage(messageId, 'skipped_synthetic_user_part');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const messagePart: Part = {
|
||||
@@ -1162,7 +1191,8 @@ export const useEventStream = () => {
|
||||
const textStr = typeof text === 'string' ? text.trim() : '';
|
||||
const shouldKeep =
|
||||
textStr.startsWith('User has requested to enter plan mode') ||
|
||||
textStr.startsWith('The plan at ');
|
||||
textStr.startsWith('The plan at ') ||
|
||||
textStr.startsWith('The following tool was executed by the user');
|
||||
if (!shouldKeep) continue;
|
||||
}
|
||||
|
||||
@@ -1457,6 +1487,11 @@ export const useEventStream = () => {
|
||||
return;
|
||||
}
|
||||
|
||||
const requestSession = useSessionStore.getState().sessions.find((session) => session.id === request.sessionID);
|
||||
if (requestSession?.parentID && requestSession.parentID === current) {
|
||||
return;
|
||||
}
|
||||
|
||||
const pending = useSessionStore
|
||||
.getState()
|
||||
.permissions
|
||||
@@ -1514,6 +1549,11 @@ export const useEventStream = () => {
|
||||
return;
|
||||
}
|
||||
|
||||
const requestSession = useSessionStore.getState().sessions.find((session) => session.id === request.sessionID);
|
||||
if (requestSession?.parentID && requestSession.parentID === current) {
|
||||
return;
|
||||
}
|
||||
|
||||
const pending = useSessionStore
|
||||
.getState()
|
||||
.questions
|
||||
|
||||
Reference in New Issue
Block a user