MCP-Server erweitert um ask_user_choice (Long-Poll), memory_read, memory_write und memory_list Tools. MCP-Config wird jetzt immer generiert (nicht nur bei Remote-Verbindung). InternalEndpoint um PendingChoices + choice-respond Endpoint ergänzt. Frontend differenziert CLI-Modus (choice-respond API) vs. API-Modus. Gesamter :::ASK_USER_CHOICE::: Text-Marker-Mechanismus aus ClaudeCliService, AnthropicService, markdown.js und stream-ui.js entfernt. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
135 lines
4.4 KiB
JavaScript
135 lines
4.4 KiB
JavaScript
// ── Streaming Bubble ──
|
||
|
||
let _streamDiv = null;
|
||
let _streamText = '';
|
||
|
||
function startStreamBubble() {
|
||
_streamDiv = document.createElement('div');
|
||
_streamDiv.className = 'msg assistant markdown streamed';
|
||
_streamDiv.innerHTML = '<p><span class="cursor"></span></p>';
|
||
_streamText = '';
|
||
chatEl().appendChild(_streamDiv);
|
||
scrollDown();
|
||
}
|
||
|
||
function appendToStream(text) {
|
||
if (!_streamDiv) startStreamBubble();
|
||
_streamText += text;
|
||
|
||
_streamDiv.innerHTML = renderMarkdown(_streamText);
|
||
// Cursor ins letzte Inline/Text-Element einfügen, damit er nicht auf eine neue Zeile rutscht
|
||
const cursor = document.createElement('span');
|
||
cursor.className = 'cursor';
|
||
const lastBlock = _streamDiv.lastElementChild;
|
||
if (lastBlock) {
|
||
lastBlock.appendChild(cursor);
|
||
} else {
|
||
_streamDiv.appendChild(cursor);
|
||
}
|
||
scrollDown();
|
||
}
|
||
|
||
function pauseStreamBubble() {
|
||
// Cursor entfernen, aber Bubble + Text behalten (z.B. bei Web-Search-Pause)
|
||
if (_streamDiv) {
|
||
const cursor = _streamDiv.querySelector('.cursor');
|
||
if (cursor) cursor.remove();
|
||
}
|
||
}
|
||
|
||
function endStreamBubble() {
|
||
if (_streamDiv) {
|
||
const cursor = _streamDiv.querySelector('.cursor');
|
||
if (cursor) cursor.remove();
|
||
if (!_streamText.trim()) _streamDiv.remove();
|
||
else {
|
||
_streamDiv.innerHTML = renderMarkdown(_streamText);
|
||
parseFluentEmojis(_streamDiv);
|
||
wrapKreaGalleries(_streamDiv);
|
||
}
|
||
}
|
||
_streamDiv = null;
|
||
_streamText = '';
|
||
}
|
||
|
||
// ── Review Bubbles (Streaming) ──
|
||
|
||
let _reviewBubble = null;
|
||
let _reviewTextEl = null;
|
||
let _reviewText = '';
|
||
|
||
function addReviewBubble(type, data, agentType) {
|
||
_reviewBubble = document.createElement('div');
|
||
_reviewBubble.className = 'msg review-bubble streamed';
|
||
if (agentType === 'finalizer') _reviewBubble.classList.add('finalizer-bubble');
|
||
if (agentType === 'preparer') _reviewBubble.classList.add('preparer-bubble');
|
||
_reviewBubble.appendChild(_createSubAgentHeader(agentType || 'review', data.agentName, data));
|
||
_reviewTextEl = document.createElement('div');
|
||
_reviewTextEl.className = 'review-content markdown';
|
||
_reviewBubble.appendChild(_reviewTextEl);
|
||
_reviewText = '';
|
||
chatEl().appendChild(_reviewBubble);
|
||
scrollDown();
|
||
}
|
||
|
||
function appendToReviewText(text) {
|
||
if (!_reviewBubble) return;
|
||
_reviewText += text;
|
||
_reviewTextEl.innerHTML = renderMarkdown(_reviewText);
|
||
scrollDown();
|
||
}
|
||
|
||
let _currentReviewToolBox = null;
|
||
|
||
function addReviewToolUse(data) {
|
||
if (!_reviewBubble) return;
|
||
if (!isToolVisible(data.toolName)) {
|
||
_currentReviewToolBox = null;
|
||
return;
|
||
}
|
||
var toolDiv = document.createElement('div');
|
||
toolDiv.className = 'tool-box review-tool-box streamed';
|
||
toolDiv.dataset.toolName = data.toolName;
|
||
var displayName = TOOL_DISPLAY_NAMES[data.toolName] || data.toolName;
|
||
toolDiv.innerHTML = '<span class="tool-name">' + esc(displayName) + '</span> <span class="tool-spinner"><i class="fa fa-hourglass-half"></i></span>';
|
||
_reviewBubble.appendChild(toolDiv);
|
||
_currentReviewToolBox = toolDiv;
|
||
scrollDown();
|
||
}
|
||
|
||
function finishReviewToolBox(data) {
|
||
if (_currentReviewToolBox) {
|
||
renderToolBoxContent(_currentReviewToolBox, data.toolName, data.input, data.result, data.isError, data.sources);
|
||
_currentReviewToolBox = null;
|
||
scrollDown();
|
||
}
|
||
}
|
||
|
||
function finalizeReviewBubble(type, data, agentType) {
|
||
if (!_reviewBubble) return;
|
||
const status = document.createElement('div');
|
||
status.className = 'review-status';
|
||
if (agentType === 'preparer' && type === 'forwarded') {
|
||
status.className += ' review-approved';
|
||
status.innerHTML = '<i class="fa fa-arrow-right"></i> Weitergeleitet an Session-Agent';
|
||
} else if (agentType === 'preparer' && type === 'query') {
|
||
status.className += ' review-rejected';
|
||
status.innerHTML = '<i class="fa fa-question-circle"></i> Rückfrage an User';
|
||
} else if (agentType === 'finalizer' || type === 'done') {
|
||
status.className += ' review-approved';
|
||
status.innerHTML = '<i class="fa fa-flag-checkered"></i> Abgeschlossen';
|
||
} else if (type === 'approved') {
|
||
status.className += ' review-approved';
|
||
status.innerHTML = '<i class="fa fa-check"></i> Approved';
|
||
} else {
|
||
status.className += ' review-rejected';
|
||
status.innerHTML = '<i class="fa fa-repeat"></i> Feedback – Worker korrigiert...';
|
||
}
|
||
_reviewBubble.appendChild(status);
|
||
_reviewBubble = null;
|
||
_reviewTextEl = null;
|
||
_reviewText = '';
|
||
_currentReviewToolBox = null;
|
||
scrollDown();
|
||
}
|