feat: add compact Codex child agent tracking

This commit is contained in:
shiyue
2026-06-16 18:16:41 +08:00
parent 51838a2ce1
commit 7e01f24e61
5 changed files with 477 additions and 93 deletions

View File

@@ -2,7 +2,7 @@
(function () {
'use strict';
const ASSET_VERSION = '20260616-composer-mcp-list';
const ASSET_VERSION = '20260616-child-agent-close-state';
const WS_URL = `${location.protocol === 'https:' ? 'wss' : 'ws'}://${location.host}/ws`;
const RENDER_DEBOUNCE = 100;
const COMPOSER_SUGGESTION_DEBOUNCE = 120;
@@ -130,6 +130,7 @@
let generatingSessionId = null;
let activeToolCalls = new Map();
let activeTodoCallTargets = new Map();
let closedCollabAgentIds = new Set();
let toolDomSeq = 0;
let toolGroupCount = 0; // 当前 .msg-tools 直接子节点数(含已有父目录)
let hasGrouped = false; // 本次输出是否已触发过折叠
@@ -785,6 +786,13 @@
return value ? value.slice(0, 8) : '';
}
function shortChildAgentId(threadId) {
const value = String(threadId || '');
if (!value) return '';
if (value.length <= 13) return value;
return `${value.slice(0, 8)}${value.slice(-4)}`;
}
function shortMessagePreview(text, maxLength = 60) {
const value = String(text || '').replace(/\s+/g, ' ').trim();
if (!value) return '空消息';
@@ -2500,6 +2508,7 @@
uploadingAttachments = [];
activeToolCalls.clear();
activeTodoCallTargets.clear();
closedCollabAgentIds = new Set();
updateGenerationControls();
chatTitle.textContent = '新会话';
updateSessionIdBadge();
@@ -2989,6 +2998,7 @@
});
}
if (msg.sessionId === currentSessionId && msg.message) {
collectClosedCollabAgentIds([msg.message]).forEach((id) => closedCollabAgentIds.add(id));
const welcome = messagesDiv.querySelector('.welcome-msg');
if (welcome) welcome.remove();
messagesDiv.appendChild(buildMsgElement(msg.message));
@@ -3167,7 +3177,12 @@
pendingText = msg.text || '';
flushRender();
if (msg.toolCalls && msg.toolCalls.length > 0) {
for (const tc of msg.toolCalls) {
const mergedCollabTool = mergeCollabAgentTools(msg.toolCalls);
const resumeToolCalls = [
...(mergedCollabTool ? [mergedCollabTool] : []),
...msg.toolCalls.filter((tc) => toolKind(tc) !== 'collab_agent_tool_call'),
];
for (const tc of resumeToolCalls) {
activeToolCalls.set(tc.id, {
name: tc.name,
input: tc.input,
@@ -3867,14 +3882,32 @@
return text.length > 140 ? `${text.slice(0, 140)}` : text;
}
function normalizeCollabAgentAction(value) {
const raw = String(value || '').trim();
if (!raw) return '';
const name = raw.split(/[./]/).filter(Boolean).pop() || raw;
const normalized = name.toLowerCase().replace(/[\s_-]/g, '');
if (normalized === 'spawn' || normalized === 'spawnagent') return 'spawn_agent';
if (normalized === 'wait' || normalized === 'waitagent') return 'wait_agent';
if (normalized === 'close' || normalized === 'closeagent') return 'close_agent';
if (normalized === 'sendinput' || normalized === 'sendmessage') return 'send_input';
if (normalized === 'resume' || normalized === 'resumeagent') return 'resume_agent';
return normalized;
}
function getCollabAgentAction(tool, data = null) {
const value = data?.tool || tool?.name || tool?.meta?.title || '';
return normalizeCollabAgentAction(value);
}
function normalizeCollabAgentData(tool) {
const inputData = effectiveObject(tool?.input);
const resultData = effectiveObject(tool?.result);
const merged = {
...inputData,
...resultData,
agentsStates: resultData.agentsStates || inputData.agentsStates || {},
receiverThreadIds: resultData.receiverThreadIds || inputData.receiverThreadIds || [],
agentsStates: resultData.agentsStates || resultData.agents_states || inputData.agentsStates || inputData.agents_states || {},
receiverThreadIds: resultData.receiverThreadIds || resultData.receiver_thread_ids || resultData.targets || inputData.receiverThreadIds || inputData.receiver_thread_ids || inputData.targets || [],
prompt: inputData.prompt || resultData.prompt || '',
tool: inputData.tool || resultData.tool || tool?.name || '',
status: resultData.status || inputData.status || tool?.meta?.status || null,
@@ -3896,12 +3929,178 @@
const state = value && typeof value === 'object' ? value : { status: value };
const label = String(state.label || state.title || state.nickname || state.name || `子代理 ${index + 1}`);
const role = String(state.role || state.agent || state.agentType || '').trim();
const status = String(state.status || state.state || 'pending').trim() || 'pending';
const detail = String(state.candidateResult || state.finalMessage || state.summary || state.lastMessage || state.step || state.description || '').trim();
let status = String(state.status || state.state || 'pending').trim() || 'pending';
if (state.closedAt && collabStateTone(status) !== 'closed') status = 'closed';
const detail = String(state.candidateResult || state.finalMessage || state.summary || state.message || state.lastMessage || state.step || state.description || '').trim();
return { id, label, role, status, detail };
});
}
function getCollabAgentIdsFromTool(tool) {
const data = normalizeCollabAgentData(tool);
const ids = new Set();
for (const entry of collabAgentStateEntries(data)) {
if (entry.id) ids.add(entry.id);
}
if (Array.isArray(data.receiverThreadIds)) {
data.receiverThreadIds.forEach((id) => {
if (id) ids.add(String(id));
});
}
const directIds = [
data.threadId,
data.thread_id,
data.agentId,
data.agent_id,
data.childThreadId,
data.child_thread_id,
data.childAgentId,
data.child_agent_id,
data.targetThreadId,
data.target_thread_id,
data.target,
];
directIds.forEach((id) => {
if (id) ids.add(String(id));
});
const directArrays = [
data.threadIds,
data.thread_ids,
data.childThreadIds,
data.child_thread_ids,
data.agentIds,
data.agent_ids,
data.targets,
];
directArrays.forEach((value) => {
if (!Array.isArray(value)) return;
value.forEach((id) => {
if (id) ids.add(String(id));
});
});
return Array.from(ids);
}
function getClosedCollabAgentIdsFromTool(tool) {
if (toolKind(tool) !== 'collab_agent_tool_call') return [];
const data = normalizeCollabAgentData(tool);
const ids = new Set();
const action = getCollabAgentAction(tool, data);
const allIds = getCollabAgentIdsFromTool(tool);
if (action === 'close_agent' || collabStateTone(data.status) === 'closed') {
allIds.forEach((id) => ids.add(id));
}
collabAgentStateEntries(data).forEach((entry) => {
if (entry.id && collabStateTone(entry.status) === 'closed') ids.add(entry.id);
});
return Array.from(ids);
}
function collectClosedCollabAgentIds(messages) {
const ids = new Set();
(Array.isArray(messages) ? messages : []).forEach((message) => {
(Array.isArray(message?.toolCalls) ? message.toolCalls : []).forEach((tool) => {
getClosedCollabAgentIdsFromTool(tool).forEach((id) => ids.add(id));
});
});
return ids;
}
function rememberClosedCollabAgentIdsFromTool(tool) {
getClosedCollabAgentIdsFromTool(tool).forEach((id) => closedCollabAgentIds.add(id));
}
function isGenericCollabAgentLabel(label, id) {
const value = String(label || '').trim();
if (!value) return true;
if (/^子代理\s*\d+$/i.test(value)) return true;
return !!id && value === String(id);
}
function mergeCollabAgentTools(tools, options = {}) {
const list = Array.isArray(tools) ? tools.filter((tool) => toolKind(tool) === 'collab_agent_tool_call') : [];
if (list.length === 0) return null;
const states = {};
const receiverThreadIds = [];
const knownClosedIds = options.closedAgentIds instanceof Set ? options.closedAgentIds : closedCollabAgentIds;
const localClosedIds = new Set(knownClosedIds || []);
let toolName = '子代';
let prompt = '';
let status = '';
let done = false;
list.forEach((tool, toolIndex) => {
const data = normalizeCollabAgentData(tool);
const action = getCollabAgentAction(tool, data);
const isCloseAction = action === 'close_agent';
const displayAction = String(data.tool || tool.name || '').trim();
if (displayAction && !['wait_agent', 'close_agent'].includes(action)) toolName = displayAction;
if (!prompt && data.prompt) prompt = data.prompt;
if (isCloseAction) {
getCollabAgentIdsFromTool(tool).forEach((id) => localClosedIds.add(id));
}
const dataStatus = isCloseAction ? 'closed' : data.status;
if (dataStatus) status = dataStatus;
done = done || !!tool.done;
collabAgentStateEntries(data).forEach((entry) => {
if (!entry.id) return;
states[entry.id] = {
...(states[entry.id] || {}),
...entry,
status: isCloseAction || localClosedIds.has(entry.id) ? 'closed' : entry.status,
};
if (collabStateTone(states[entry.id].status) === 'closed') localClosedIds.add(entry.id);
});
getCollabAgentIdsFromTool(tool).forEach((id) => {
if (!receiverThreadIds.includes(id)) receiverThreadIds.push(id);
states[id] = {
...(states[id] || {}),
label: states[id]?.label || `子代理 ${receiverThreadIds.length}`,
status: isCloseAction || localClosedIds.has(id)
? 'closed'
: (data.status || states[id]?.status || (tool.done ? 'completed' : 'running')),
};
});
if (receiverThreadIds.length === 0 && list.length === 1) {
const fallbackId = tool.id || `tool-${toolIndex + 1}`;
receiverThreadIds.push(fallbackId);
states[fallbackId] = {
label: '子代理',
status: isCloseAction ? 'closed' : (data.status || (tool.done ? 'completed' : 'running')),
};
}
});
receiverThreadIds.forEach((id, index) => {
states[id] = {
...(states[id] || {}),
label: states[id]?.label || `子代理 ${index + 1}`,
status: localClosedIds.has(id) ? 'closed' : (states[id]?.status || 'pending'),
};
});
const allClosed = receiverThreadIds.length > 0
&& receiverThreadIds.every((id) => collabStateTone(states[id]?.status) === 'closed');
const mergedStatus = allClosed ? 'closed' : (status || (done ? 'completed' : 'running'));
return {
id: list[0].id || 'collab-agent-merged',
name: list[0].name || 'ccweb_mcp_child_agent',
kind: 'collab_agent_tool_call',
done,
input: {
tool: toolName,
prompt,
status: mergedStatus,
receiverThreadIds,
agentsStates: states,
},
};
}
function collabStateTone(statusText) {
const normalized = String(statusText || '').toLowerCase();
if (!normalized) return 'pending';
@@ -3934,6 +4133,12 @@
const stack = document.createElement('div');
stack.className = 'collab-agent-stack';
const stateEntries = collabAgentStateEntries(data);
const threadCount = Array.isArray(data.receiverThreadIds) ? data.receiverThreadIds.length : 0;
const agentCount = stateEntries.length;
const totalCount = agentCount || threadCount || 0;
const promptText = summarizePrompt(data.prompt);
const header = document.createElement('div');
header.className = 'collab-agent-header';
@@ -3942,55 +4147,71 @@
const kicker = document.createElement('div');
kicker.className = 'collab-agent-kicker';
kicker.textContent = 'ccweb MCP 子代';
kicker.textContent = '子代';
titleWrap.appendChild(kicker);
const title = document.createElement('div');
title.className = 'collab-agent-title';
title.textContent = data.tool || '协作任务';
title.textContent = `${totalCount || 0}`;
titleWrap.appendChild(title);
const meta = document.createElement('div');
meta.className = 'collab-agent-meta';
const threadCount = Array.isArray(data.receiverThreadIds) ? data.receiverThreadIds.length : 0;
const agentCount = collabAgentStateEntries(data).length;
meta.textContent = `${agentCount || threadCount || 0} 个子代理`;
titleWrap.appendChild(meta);
meta.textContent = '';
if (promptText) meta.title = promptText;
if (promptText) titleWrap.appendChild(meta);
header.appendChild(titleWrap);
const headerActions = document.createElement('div');
headerActions.className = 'collab-agent-actions';
const statusChip = document.createElement('span');
const overallTone = collabStateTone(data.status || (tool.done ? 'completed' : 'running'));
statusChip.className = `collab-agent-overall-status ${overallTone}`;
statusChip.textContent = collabStateLabel(data.status || (tool.done ? 'completed' : 'running'));
header.appendChild(statusChip);
headerActions.appendChild(statusChip);
header.appendChild(headerActions);
stack.appendChild(header);
const promptText = summarizePrompt(data.prompt);
if (promptText) {
const promptBlock = document.createElement('div');
promptBlock.className = 'collab-agent-prompt';
promptBlock.textContent = promptText;
stack.appendChild(promptBlock);
}
const stateEntries = collabAgentStateEntries(data);
if (stateEntries.length > 0) {
const list = document.createElement('div');
list.className = 'collab-agent-list';
stateEntries.forEach((entry, index) => {
const tone = collabStateTone(entry.status);
const item = document.createElement('div');
item.className = 'collab-agent-item';
item.title = [
entry.label || `子代理 ${index + 1}`,
entry.role ? `角色: ${entry.role}` : '',
entry.detail ? `结果: ${entry.detail}` : '',
entry.id ? `ID: ${entry.id}` : '',
].filter(Boolean).join('\n');
if (entry.id) {
item.setAttribute('role', 'button');
item.tabIndex = 0;
item.addEventListener('click', () => {
copyTextToClipboard(entry.id, '子代理线程 ID 已复制');
});
item.addEventListener('keydown', (event) => {
if (event.key === 'Enter' || event.key === ' ') {
event.preventDefault();
copyTextToClipboard(entry.id, '子代理线程 ID 已复制');
}
});
}
const row = document.createElement('div');
row.className = 'collab-agent-item-row';
const label = document.createElement('div');
label.className = 'collab-agent-item-label';
label.textContent = entry.label || `子代理 ${index + 1}`;
label.textContent = !isGenericCollabAgentLabel(entry.label, entry.id)
? entry.label
: `ID ${shortChildAgentId(entry.id || '')}`;
row.appendChild(label);
const chip = document.createElement('span');
const tone = collabStateTone(entry.status);
chip.className = `collab-agent-item-status ${tone}`;
chip.textContent = collabStateLabel(entry.status);
row.appendChild(chip);
@@ -4015,28 +4236,11 @@
}
item.appendChild(row);
if (entry.role) {
const role = document.createElement('div');
role.className = 'collab-agent-item-role';
role.textContent = entry.role;
item.appendChild(role);
}
if (entry.detail) {
const detail = document.createElement('div');
detail.className = 'collab-agent-item-detail';
detail.textContent = entry.detail;
item.appendChild(detail);
}
if (entry.id) {
if (entry.id || entry.role) {
const footer = document.createElement('div');
footer.className = 'collab-agent-item-footer';
footer.textContent = `ID ${shortSessionId(entry.id)}`;
footer.title = entry.id;
footer.addEventListener('click', () => {
copyTextToClipboard(entry.id, '子代理线程 ID 已复制');
});
footer.textContent = entry.role || '';
if (!footer.textContent) footer.hidden = true;
item.appendChild(footer);
}
@@ -4045,14 +4249,14 @@
stack.appendChild(list);
}
if (Array.isArray(data.receiverThreadIds) && data.receiverThreadIds.length > 0) {
if (stateEntries.length === 0 && Array.isArray(data.receiverThreadIds) && data.receiverThreadIds.length > 0) {
const threads = document.createElement('div');
threads.className = 'collab-agent-threads';
data.receiverThreadIds.forEach((threadId) => {
const btn = document.createElement('button');
btn.type = 'button';
btn.className = 'collab-agent-thread-chip';
btn.textContent = `ID ${shortSessionId(threadId)}`;
btn.textContent = `ID ${shortChildAgentId(threadId)}`;
btn.title = `复制子代理线程 ID\n${threadId}`;
btn.addEventListener('click', () => {
copyTextToClipboard(threadId, '子代理线程 ID 已复制');
@@ -4108,7 +4312,12 @@
const bubble = el.querySelector('.msg-bubble');
const FOLD_AT = 3;
let grouped = false;
for (const tc of m.toolCalls) {
const mergedCollabTool = mergeCollabAgentTools(m.toolCalls);
const renderToolCalls = [
...(mergedCollabTool ? [mergedCollabTool] : []),
...m.toolCalls.filter((tc) => toolKind(tc) !== 'collab_agent_tool_call'),
];
for (const tc of renderToolCalls) {
if (isEmptyReasoningTool(tc)) continue;
const details = createToolCallElement(tc.id || `saved-${Math.random().toString(36).slice(2)}`, tc, true);
@@ -4153,6 +4362,7 @@
function renderMessages(messages, options = {}) {
renderEpoch++;
const epoch = renderEpoch;
closedCollabAgentIds = collectClosedCollabAgentIds(messages);
messagesDiv.innerHTML = '';
clearUserMessageIndex();
if (messages.length === 0) {
@@ -4216,6 +4426,7 @@
function prependHistoryMessages(messages, options = {}) {
if (!Array.isArray(messages) || messages.length === 0) return;
collectClosedCollabAgentIds(messages).forEach((id) => closedCollabAgentIds.add(id));
const preserveScroll = options.preserveScroll !== false;
const skipScrollbar = options.skipScrollbar === true;
const welcome = messagesDiv.querySelector('.welcome-msg');
@@ -4477,6 +4688,7 @@
wrapper.dataset.toolUseId = toolUseId ? String(toolUseId) : '';
wrapper.dataset.toolName = tool.name || '';
wrapper.dataset.toolKind = kind;
wrapper.dataset.childIds = getCollabAgentIdsFromTool(tool).join(',');
wrapper.appendChild(buildToolContentElement({ ...tool, done }));
return wrapper;
}
@@ -4508,6 +4720,47 @@
return details;
}
function upsertCollabAgentToolCall(toolsDiv, toolUseId, tool, done) {
if (!toolsDiv || !tool) return null;
const existing = toolsDiv.querySelector(':scope > .ccweb-mcp-child-agent-tool-call[data-collab-merged="true"]')
|| toolsDiv.querySelector(':scope > .tool-group .ccweb-mcp-child-agent-tool-call[data-collab-merged="true"]');
const nextTool = { ...tool, id: toolUseId, done };
rememberClosedCollabAgentIdsFromTool(nextTool);
const map = existing?.__collabTools instanceof Map ? existing.__collabTools : new Map();
map.set(toolUseId || nextTool.id || `collab-${map.size + 1}`, nextTool);
const merged = mergeCollabAgentTools(Array.from(map.values()));
if (!merged) return existing;
if (existing) {
existing.__collabTools = map;
existing.dataset.toolUseId = merged.id || existing.dataset.toolUseId || '';
existing.dataset.childIds = getCollabAgentIdsFromTool(merged).join(',');
existing.replaceChildren(buildToolContentElement(merged));
removeDuplicateCollabAgentNodes(toolsDiv);
return existing;
}
const el = createToolCallElement(merged.id, merged, !!merged.done);
el.dataset.collabMerged = 'true';
el.dataset.childIds = getCollabAgentIdsFromTool(merged).join(',');
el.__collabTools = map;
toolsDiv.appendChild(el);
removeDuplicateCollabAgentNodes(toolsDiv);
return el;
}
function removeDuplicateCollabAgentNodes(scope) {
if (!scope) return;
const seen = new Set();
const nodes = Array.from(scope.querySelectorAll('.ccweb-mcp-child-agent-tool-call'));
nodes.forEach((node) => {
const ids = String(node.dataset.childIds || '').split(',').filter(Boolean);
const duplicate = ids.length > 0 && ids.some((id) => seen.has(id));
ids.forEach((id) => seen.add(id));
if (duplicate) node.remove();
});
}
function appendToolCall(toolUseId, name, input, done, kind = null, meta = null, result = undefined) {
const streamEl = document.getElementById('streaming-msg');
if (!streamEl) return;
@@ -4519,6 +4772,12 @@
const tool = { id: toolUseId, name, input, kind, meta, done };
if (result !== undefined) tool.result = result;
if (isEmptyReasoningTool(tool)) return;
if (toolKind(tool) === 'collab_agent_tool_call') {
const el = upsertCollabAgentToolCall(toolsDiv, toolUseId, tool, done);
if (el) rememberToolCallTarget(toolUseId, tool, el);
scrollToBottom();
return;
}
// 如果是 todo_list检查是否已存在相同 id 的 todo_list
if (kind === 'todo_list' && input?.id) {
@@ -4592,6 +4851,13 @@
const tool = activeToolCalls.get(toolUseId) || null;
const toolUseIdText = toolUseId ? String(toolUseId) : '';
const scope = getLatestAssistantToolScope();
if (toolKind(tool) === 'collab_agent_tool_call') {
const toolsDiv = scope?.querySelector?.('.msg-tools') || scope?.querySelector?.('.msg-bubble') || scope;
const nextTool = { ...tool, result, done };
const el = upsertCollabAgentToolCall(toolsDiv, toolUseId, nextTool, done);
if (el) rememberToolCallTarget(toolUseId, nextTool, el);
return;
}
let el = tool?.domElement && tool.domElement.isConnected ? tool.domElement : null;
if (!el) {
@@ -4649,6 +4915,13 @@
const tool = msg?.tool;
const toolUseId = msg?.toolUseId || tool?.id;
if (!toolUseId || !tool) return;
const isCurrentSessionUpdate = msg.sessionId === currentSessionId;
if (isCurrentSessionUpdate) {
if (msg?.child?.threadId && collabStateTone(msg.child.status) === 'closed') {
closedCollabAgentIds.add(String(msg.child.threadId));
}
rememberClosedCollabAgentIdsFromTool(tool);
}
updateCachedSession(msg.sessionId, (snapshot) => {
const messages = Array.isArray(snapshot.messages) ? snapshot.messages : [];
@@ -4667,7 +4940,7 @@
}
});
if (msg.sessionId !== currentSessionId) return;
if (!isCurrentSessionUpdate) return;
activeToolCalls.set(toolUseId, {
id: toolUseId,
name: tool.name,

View File

@@ -14,7 +14,7 @@
document.documentElement.dataset.dividerTime = dividerTime;
})();
</script>
<link rel="stylesheet" href="style.css?v=20260616-runtime-insert-controls">
<link rel="stylesheet" href="style.css?v=20260616-child-agent-close-state">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/atom-one-dark.min.css">
</head>
<body>
@@ -150,6 +150,6 @@
<script src="https://cdnjs.cloudflare.com/ajax/libs/marked/12.0.1/marked.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
<script src="app.js?v=20260616-runtime-insert-controls"></script>
<script src="app.js?v=20260616-child-agent-close-state"></script>
</body>
</html>

View File

@@ -2334,11 +2334,18 @@ html[data-divider-time='hide'] .msg-bubble .agent-message-divider span {
.code-block-wrapper.preview-mode pre { display: none; }
/* Tool calls */
.msg-tools {
display: flex;
flex-wrap: wrap;
align-items: flex-start;
gap: 4px 6px;
}
.tool-call {
margin: 8px 0;
border: 1px solid var(--border-color);
border-radius: 10px;
overflow: hidden;
width: 100%;
}
.tool-call.codex-command {
border-color: rgba(91, 126, 161, 0.24);
@@ -2351,12 +2358,16 @@ html[data-divider-time='hide'] .msg-bubble .agent-message-divider span {
border-color: rgba(93, 138, 84, 0.24);
}
.tool-call.ccweb-mcp-child-agent-tool-call {
border-color: rgba(91, 126, 161, 0.28);
background:
linear-gradient(180deg, rgba(255, 255, 255, 0.66), rgba(91, 126, 161, 0.04));
border-color: transparent;
background: transparent;
}
.tool-call.ccweb-mcp-child-agent-tool-call.collab-agent-inline {
display: inline-flex;
width: auto;
max-width: 100%;
margin: 0;
overflow: visible;
border-radius: 0;
}
.tool-call summary {
padding: 8px 12px;
@@ -2468,8 +2479,8 @@ html[data-divider-time='hide'] .msg-bubble .agent-message-divider span {
word-break: normal;
max-height: none;
overflow: visible;
background:
linear-gradient(180deg, rgba(252, 253, 255, 0.96), rgba(242, 247, 252, 0.98));
padding: 0;
background: transparent;
color: var(--text-primary);
}
.tool-call-content.todo-list-content .todo-list-container {
@@ -2519,6 +2530,7 @@ html[data-divider-time='hide'] .msg-bubble .agent-message-divider span {
border: 1px solid var(--border-color);
border-radius: 10px;
overflow: hidden;
width: 100%;
}
.tool-group-summary {
padding: 8px 12px;
@@ -2539,12 +2551,19 @@ html[data-divider-time='hide'] .msg-bubble .agent-message-divider span {
.tool-group[open] > .tool-group-summary::before { transform: rotate(90deg); }
.tool-group-summary:hover { background: var(--bg-tertiary); }
.tool-group-inner {
display: flex;
flex-wrap: wrap;
align-items: flex-start;
gap: 4px 6px;
padding: 4px 8px;
background: var(--bg-primary);
}
.tool-group-inner .tool-call {
margin: 4px 0;
}
.tool-group-inner .tool-call.ccweb-mcp-child-agent-tool-call.collab-agent-inline {
margin: 0;
}
/* AskUserQuestion preview */
.ask-user-question {
@@ -4237,48 +4256,65 @@ html[data-theme='coolvibe'] .settings-back:hover {
}
.collab-agent-stack {
display: flex;
display: inline-flex;
flex-direction: column;
gap: 10px;
gap: 4px;
min-width: 0;
}
.collab-agent-header {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 10px;
align-items: center;
justify-content: flex-start;
gap: 4px;
min-width: 0;
}
.collab-agent-title-wrap {
min-width: 0;
display: flex;
flex-direction: column;
gap: 2px;
flex-direction: row;
align-items: baseline;
gap: 4px;
overflow: hidden;
}
.collab-agent-kicker {
font-size: 10px;
flex-shrink: 0;
font-size: 11px;
font-weight: 800;
letter-spacing: 0.08em;
letter-spacing: 0.02em;
text-transform: uppercase;
color: var(--info);
}
.collab-agent-title {
font-size: 14px;
min-width: 0;
max-width: 56px;
font-size: 12px;
font-weight: 700;
line-height: 1.35;
line-height: 1.2;
color: var(--text-primary);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.collab-agent-meta {
font-size: 11px;
flex-shrink: 0;
font-size: 12px;
color: var(--text-muted);
}
.collab-agent-actions {
display: inline-flex;
align-items: center;
gap: 4px;
flex-shrink: 0;
}
.collab-agent-overall-status,
.collab-agent-item-status {
display: inline-flex;
align-items: center;
justify-content: center;
min-height: 24px;
padding: 0 9px;
min-height: 20px;
padding: 0 7px;
border-radius: 999px;
font-size: 10px;
font-size: 12px;
font-weight: 800;
line-height: 1;
white-space: nowrap;
@@ -4319,31 +4355,41 @@ html[data-theme='coolvibe'] .settings-back:hover {
}
.collab-agent-list {
display: flex;
flex-direction: column;
gap: 8px;
flex-wrap: wrap;
gap: 4px;
}
.collab-agent-item {
display: flex;
flex-direction: column;
gap: 6px;
padding: 10px 12px;
border: 1px solid rgba(91, 126, 161, 0.14);
border-radius: 10px;
background: rgba(255, 255, 255, 0.74);
appearance: none;
max-width: 100%;
min-height: 40px;
display: inline-flex;
flex-direction: row;
align-items: center;
gap: 4px;
padding: 5px 10px;
border: 1px solid rgba(91, 126, 161, 0.16);
border-radius: 7px;
background: rgba(255, 255, 255, 0.58);
color: inherit;
font: inherit;
cursor: pointer;
text-align: left;
}
.collab-agent-item-row {
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
gap: 4px;
}
.collab-agent-item-label {
min-width: 0;
flex: 1;
max-width: 150px;
font-size: 13px;
font-weight: 700;
color: var(--text-primary);
line-height: 1.35;
line-height: 1.2;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.collab-agent-item-role {
font-size: 11px;
@@ -4356,10 +4402,10 @@ html[data-theme='coolvibe'] .settings-back:hover {
line-height: 1.55;
}
.collab-agent-item-footer {
align-self: flex-start;
font-size: 11px;
color: var(--text-muted);
cursor: pointer;
line-height: 1.1;
display: none;
}
.collab-agent-item-footer:hover {
color: var(--info);
@@ -4371,9 +4417,10 @@ html[data-theme='coolvibe'] .settings-back:hover {
border-radius: 999px;
background: rgba(255, 255, 255, 0.86);
color: var(--text-secondary);
padding: 4px 9px;
min-height: 22px;
padding: 0 8px;
font: inherit;
font-size: 11px;
font-size: 12px;
font-weight: 800;
cursor: pointer;
}
@@ -4448,11 +4495,18 @@ html[data-theme='coolvibe'] .settings-back:hover {
opacity: 0.6;
}
@media (max-width: 640px) {
.collab-agent-header,
.collab-agent-item-row {
flex-direction: column;
.collab-agent-header {
align-items: flex-start;
}
.collab-agent-title-wrap {
flex-wrap: wrap;
}
.collab-agent-actions {
margin-left: auto;
}
.collab-agent-item-label {
max-width: 96px;
}
}
.import-item-btn:hover { background: var(--accent-hover); }
@@ -4942,8 +4996,8 @@ html[data-theme='coolvibe'] .settings-back:hover {
:is(html[data-theme='carbon'], html[data-theme='nocturne'], html[data-theme='cinder']) .tool-call.codex-command,
:is(html[data-theme='carbon'], html[data-theme='nocturne'], html[data-theme='cinder']) .tool-call.ccweb-mcp-child-agent-tool-call {
background: var(--dark-panel-bg);
border-color: var(--note-border);
background: transparent;
border-color: transparent;
box-shadow: none;
}
@@ -4970,6 +5024,10 @@ html[data-theme='coolvibe'] .settings-back:hover {
color: var(--text-primary);
}
:is(html[data-theme='carbon'], html[data-theme='nocturne'], html[data-theme='cinder']) .tool-call-content.collab-agent-content {
background: transparent;
}
:is(html[data-theme='carbon'], html[data-theme='nocturne'], html[data-theme='cinder']) .tool-call-state,
:is(html[data-theme='carbon'], html[data-theme='nocturne'], html[data-theme='cinder']) .session-item-status,
:is(html[data-theme='carbon'], html[data-theme='nocturne'], html[data-theme='cinder']) .collab-agent-overall-status,