fix: refine memory monitor scrolling and snapshots
This commit is contained in:
parent
ca8f65fe35
commit
9183e0caf0
@ -301,7 +301,8 @@ const appOptions = {
|
||||
return this.streamingMessage || this.hasPendingToolActions();
|
||||
},
|
||||
composerBusy() {
|
||||
return this.streamingUi || this.monitorIsLocked || this.stopRequested;
|
||||
const monitorLock = this.monitorIsLocked && this.chatDisplayMode === 'monitor';
|
||||
return this.streamingUi || monitorLock || this.stopRequested;
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@ -130,21 +130,14 @@
|
||||
|
||||
<div class="window memory-window" ref="memoryWindow">
|
||||
<div class="window-header">
|
||||
<div>
|
||||
<span class="traffic-dot red"></span>
|
||||
<span class="traffic-dot yellow"></span>
|
||||
<span class="traffic-dot green"></span>
|
||||
<span>记忆记录</span>
|
||||
</div>
|
||||
<span ref="memoryStatus">同步待命</span>
|
||||
<span class="traffic-dot red"></span>
|
||||
<span class="traffic-dot yellow"></span>
|
||||
<span class="traffic-dot green"></span>
|
||||
<span class="window-title">记忆</span>
|
||||
</div>
|
||||
<div class="memory-body">
|
||||
<div class="memory-list" ref="memoryList"></div>
|
||||
</div>
|
||||
<div class="memory-footer">
|
||||
<span>总计 <strong ref="memoryCount">0</strong> 条</span>
|
||||
<span>最后更新 <strong ref="memoryTime">--:--</strong></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="window todo-window" ref="todoWindow">
|
||||
|
||||
@ -232,6 +232,7 @@ export class MonitorDirector implements MonitorDriver {
|
||||
private progressBubbleTimer: number | null = null;
|
||||
private progressBubbleBase: string | null = null;
|
||||
private progressSceneName: string | null = null;
|
||||
private latestMemoryScroll = 0;
|
||||
private thinkingBubbleTimer: number | null = null;
|
||||
private thinkingBubblePhase = 0;
|
||||
private waitingBubbleTimer: number | null = null;
|
||||
@ -343,7 +344,6 @@ export class MonitorDirector implements MonitorDriver {
|
||||
this.terminalLastFocusedAt = 0;
|
||||
this.elements.readerLines.innerHTML = '';
|
||||
this.elements.readerOcr.innerHTML = '';
|
||||
this.elements.memoryList.innerHTML = '';
|
||||
this.elements.todoBacklog.innerHTML = '';
|
||||
this.elements.todoDoing.innerHTML = '';
|
||||
this.elements.todoDone.innerHTML = '';
|
||||
@ -1379,6 +1379,244 @@ export class MonitorDirector implements MonitorDriver {
|
||||
}
|
||||
}
|
||||
|
||||
private async ensureMemoryWindowVisible(options: { initialEntries?: string[]; memoryType?: string } = {}) {
|
||||
const { initialEntries = [], memoryType = 'main' } = options;
|
||||
const hydrate = async () => {
|
||||
if (initialEntries.length) {
|
||||
this.renderMemoryEntries(initialEntries);
|
||||
return;
|
||||
}
|
||||
if (!this.getMemoryItems().length) {
|
||||
await this.loadMemoryEntries(memoryType);
|
||||
}
|
||||
};
|
||||
if (this.isWindowVisible(this.elements.memoryWindow)) {
|
||||
this.showWindow(this.elements.memoryWindow);
|
||||
if (!this.getMemoryItems().length || initialEntries.length) {
|
||||
await hydrate();
|
||||
}
|
||||
return;
|
||||
}
|
||||
await this.movePointerToApp('memory');
|
||||
await this.click();
|
||||
this.showWindow(this.elements.memoryWindow);
|
||||
await hydrate();
|
||||
}
|
||||
|
||||
private getMemoryItems(): HTMLElement[] {
|
||||
if (!this.elements.memoryList) return [];
|
||||
return Array.from(this.elements.memoryList.children) as HTMLElement[];
|
||||
}
|
||||
|
||||
private extractMemorySnapshotEntries(payload: any, stage: 'before' | 'after' = 'before'): string[] {
|
||||
const key = stage === 'after' ? 'monitor_snapshot_after' : 'monitor_snapshot';
|
||||
const snapshot = payload?.[key];
|
||||
const entries = snapshot?.entries;
|
||||
if (Array.isArray(entries)) {
|
||||
return entries.map(entry => String(entry ?? ''));
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
private getMemoryItemByIndex(index: number): HTMLElement | null {
|
||||
const items = this.getMemoryItems();
|
||||
if (!index || index < 1 || index > items.length) {
|
||||
return null;
|
||||
}
|
||||
return items[index - 1];
|
||||
}
|
||||
|
||||
private async waitForScrollSettled(el: HTMLElement, targetTop: number, timeout = 600) {
|
||||
const started = Date.now();
|
||||
return new Promise<void>(resolve => {
|
||||
const tick = () => {
|
||||
const arrived = Math.abs(el.scrollTop - targetTop) < 2;
|
||||
const expired = Date.now() - started > timeout;
|
||||
if (arrived || expired) {
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
requestAnimationFrame(tick);
|
||||
};
|
||||
requestAnimationFrame(tick);
|
||||
});
|
||||
}
|
||||
|
||||
private async scrollMemoryToBottom(waitMs = 200) {
|
||||
const body = this.elements.memoryList;
|
||||
if (!body) return;
|
||||
const target = body.scrollHeight;
|
||||
body.scrollTo({ top: target, behavior: 'smooth' });
|
||||
await this.waitForScrollSettled(body, target);
|
||||
this.latestMemoryScroll = Date.now();
|
||||
if (waitMs > 0) {
|
||||
await sleep(waitMs);
|
||||
}
|
||||
}
|
||||
|
||||
private async scrollMemoryItemIntoView(card: HTMLElement | null, waitMs = 200) {
|
||||
if (!card) return;
|
||||
const body = this.elements.memoryList;
|
||||
if (!body) return;
|
||||
const targetTop = card.offsetTop - body.clientHeight * 0.35;
|
||||
const clamped = Math.max(0, targetTop);
|
||||
body.scrollTo({ top: clamped, behavior: 'smooth' });
|
||||
await this.waitForScrollSettled(body, clamped);
|
||||
this.latestMemoryScroll = Date.now();
|
||||
if (waitMs > 0) {
|
||||
await sleep(waitMs);
|
||||
}
|
||||
}
|
||||
|
||||
private ensureMemoryTypingVisible(card: HTMLElement | null) {
|
||||
if (!card) return;
|
||||
const body = this.elements.memoryList;
|
||||
if (!body) return;
|
||||
const bodyTop = body.scrollTop;
|
||||
const bodyBottom = bodyTop + body.clientHeight;
|
||||
const cardTop = card.offsetTop;
|
||||
const cardBottom = cardTop + card.offsetHeight;
|
||||
// 如果底部被遮挡,向下滚动到露出多 12px 缓冲
|
||||
if (cardBottom > bodyBottom - 4) {
|
||||
const delta = cardBottom - bodyBottom + 12;
|
||||
body.scrollTop = bodyTop + delta;
|
||||
this.latestMemoryScroll = Date.now();
|
||||
} else if (cardTop < bodyTop) {
|
||||
// 若顶部超出,滚回顶部
|
||||
body.scrollTop = Math.max(0, cardTop - 8);
|
||||
this.latestMemoryScroll = Date.now();
|
||||
}
|
||||
}
|
||||
|
||||
private highlightMemoryCard(card: HTMLElement, active = true) {
|
||||
if (!card) return;
|
||||
card.classList.toggle('editing', !!active);
|
||||
}
|
||||
|
||||
private updateMemoryMeta() {
|
||||
if (!this.elements.memoryList) return;
|
||||
const count = this.elements.memoryList.children.length;
|
||||
if (this.elements.memoryCount) {
|
||||
this.elements.memoryCount.textContent = String(count);
|
||||
}
|
||||
if (this.elements.memoryStatus) {
|
||||
this.elements.memoryStatus.textContent = '记忆已同步';
|
||||
}
|
||||
if (this.elements.memoryTime) {
|
||||
this.elements.memoryTime.textContent = new Date().toLocaleTimeString('zh-CN', {
|
||||
hour: '2-digit',
|
||||
minute: '2-digit'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private createMemoryCard(text: string) {
|
||||
const item = document.createElement('div');
|
||||
item.className = 'memory-item new';
|
||||
const body = document.createElement('div');
|
||||
body.className = 'memory-text';
|
||||
body.textContent = text;
|
||||
item.appendChild(body);
|
||||
return item;
|
||||
}
|
||||
|
||||
private async typeIntoMemoryCard(card: HTMLElement, text: string, options: { clearFirst?: boolean } = {}) {
|
||||
if (!card) return;
|
||||
const body = card.querySelector('.memory-text') as HTMLElement | null;
|
||||
if (!body) return;
|
||||
if (options.clearFirst) {
|
||||
const existing = body.textContent || '';
|
||||
for (let i = existing.length; i > 0; i--) {
|
||||
body.textContent = existing.slice(0, i - 1);
|
||||
await sleep(18);
|
||||
}
|
||||
}
|
||||
const chars = Array.from(text);
|
||||
let idx = 0;
|
||||
for (const ch of chars) {
|
||||
idx += 1;
|
||||
body.textContent = (body.textContent || '') + ch;
|
||||
if (idx % 4 === 0) {
|
||||
this.ensureMemoryTypingVisible(card);
|
||||
}
|
||||
await sleep(18);
|
||||
}
|
||||
// 最终再校准一次,确保结尾可见
|
||||
this.ensureMemoryTypingVisible(card);
|
||||
}
|
||||
|
||||
private async animateMemoryAppend(text: string) {
|
||||
if (!this.elements.memoryList) return;
|
||||
await this.scrollMemoryToBottom();
|
||||
const card = this.createMemoryCard('');
|
||||
this.elements.memoryList.appendChild(card);
|
||||
requestAnimationFrame(() => card.classList.add('visible'));
|
||||
await sleep(160);
|
||||
await this.scrollMemoryToBottom();
|
||||
await this.movePointerToElement(card, { duration: 420 });
|
||||
await this.click();
|
||||
this.highlightMemoryCard(card, true);
|
||||
await this.typeIntoMemoryCard(card, text, { clearFirst: true });
|
||||
// 若内容超出单行,确保滚动至卡片底部
|
||||
await this.scrollMemoryItemIntoView(card);
|
||||
this.highlightMemoryCard(card, false);
|
||||
}
|
||||
|
||||
private async animateMemoryReplace(index: number, text: string) {
|
||||
const card = this.getMemoryItemByIndex(index);
|
||||
if (!card) {
|
||||
await this.animateMemoryAppend(text);
|
||||
return;
|
||||
}
|
||||
await this.scrollMemoryItemIntoView(card);
|
||||
await this.movePointerToElement(card, { duration: 420 });
|
||||
await this.click();
|
||||
this.highlightMemoryCard(card, true);
|
||||
await this.typeIntoMemoryCard(card, text, { clearFirst: true });
|
||||
await this.scrollMemoryItemIntoView(card);
|
||||
this.highlightMemoryCard(card, false);
|
||||
}
|
||||
|
||||
private async animateMemoryDelete(index: number) {
|
||||
const card = this.getMemoryItemByIndex(index);
|
||||
if (!card || !this.elements.memoryList) {
|
||||
return;
|
||||
}
|
||||
await this.scrollMemoryItemIntoView(card);
|
||||
await this.movePointerToElement(card, { duration: 360 });
|
||||
await this.click();
|
||||
this.highlightMemoryCard(card, true);
|
||||
card.classList.add('swipe-out');
|
||||
await this.movePointerToElement(card, { offsetX: card.clientWidth * 0.45, duration: 240 });
|
||||
await sleep(200);
|
||||
card.remove();
|
||||
this.highlightMemoryCard(card, false);
|
||||
}
|
||||
|
||||
private async loadMemoryEntries(memoryType = 'main') {
|
||||
try {
|
||||
const resp = await fetch(`/api/memory?type=${encodeURIComponent(memoryType)}`);
|
||||
const data = await resp.json();
|
||||
if (!data?.success || !Array.isArray(data.entries)) {
|
||||
return;
|
||||
}
|
||||
this.renderMemoryEntries(data.entries);
|
||||
} catch (error) {
|
||||
console.warn('loadMemoryEntries failed', error);
|
||||
}
|
||||
}
|
||||
|
||||
private renderMemoryEntries(entries: string[]) {
|
||||
if (!this.elements.memoryList) return;
|
||||
this.elements.memoryList.innerHTML = '';
|
||||
entries.forEach(text => {
|
||||
const card = this.createMemoryCard(text);
|
||||
card.classList.add('visible');
|
||||
this.elements.memoryList.appendChild(card);
|
||||
});
|
||||
this.updateMemoryMeta();
|
||||
}
|
||||
|
||||
private async revealTerminalWindow(instance: TerminalShell, title: string) {
|
||||
await this.movePointerToApp('terminal');
|
||||
await this.click();
|
||||
@ -2509,12 +2747,25 @@ export class MonitorDirector implements MonitorDriver {
|
||||
|
||||
this.sceneHandlers.memoryUpdate = async (payload, runtime) => {
|
||||
this.applySceneStatus(runtime, 'memoryUpdate', '正在同步记忆');
|
||||
await this.movePointerToApp('memory');
|
||||
await this.click({ count: 2 });
|
||||
this.showWindow(this.elements.memoryWindow);
|
||||
const content = payload?.arguments?.content || payload?.arguments?.memory || payload?.result?.content || '记忆内容';
|
||||
this.addMemoryCard(content);
|
||||
await sleep(500);
|
||||
const initialEntries = this.extractMemorySnapshotEntries(payload, 'before');
|
||||
const memoryType =
|
||||
(payload?.arguments?.memory_type || payload?.result?.memory_type || 'main').toString().toLowerCase();
|
||||
await this.ensureMemoryWindowVisible({ initialEntries, memoryType });
|
||||
|
||||
const op = (payload?.arguments?.operation || payload?.result?.operation || 'append').toLowerCase();
|
||||
const content = payload?.arguments?.content || payload?.result?.content || '';
|
||||
const index = Number(payload?.arguments?.index || payload?.result?.index || 0) || 0;
|
||||
|
||||
if (op === 'replace') {
|
||||
await this.animateMemoryReplace(index, content || '');
|
||||
} else if (op === 'delete') {
|
||||
await this.animateMemoryDelete(index);
|
||||
} else {
|
||||
await this.animateMemoryAppend(content || '新记忆');
|
||||
}
|
||||
|
||||
this.updateMemoryMeta();
|
||||
await sleep(360);
|
||||
};
|
||||
|
||||
this.sceneHandlers.todoCreate = async (payload, runtime) => {
|
||||
@ -4732,22 +4983,10 @@ export class MonitorDirector implements MonitorDriver {
|
||||
}
|
||||
|
||||
private addMemoryCard(text: string) {
|
||||
const item = document.createElement('div');
|
||||
item.className = 'memory-item new';
|
||||
const tag = document.createElement('div');
|
||||
tag.className = 'memory-tag';
|
||||
tag.textContent = 'MEM';
|
||||
const body = document.createElement('div');
|
||||
body.className = 'memory-text';
|
||||
body.textContent = text;
|
||||
item.appendChild(tag);
|
||||
item.appendChild(body);
|
||||
this.elements.memoryList.appendChild(item);
|
||||
requestAnimationFrame(() => item.classList.add('visible'));
|
||||
const count = this.elements.memoryList.children.length;
|
||||
this.elements.memoryCount.textContent = String(count);
|
||||
this.elements.memoryStatus.textContent = '记忆已同步';
|
||||
this.elements.memoryTime.textContent = new Date().toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit' });
|
||||
const card = this.createMemoryCard(text);
|
||||
this.elements.memoryList.appendChild(card);
|
||||
requestAnimationFrame(() => card.classList.add('visible'));
|
||||
this.updateMemoryMeta();
|
||||
}
|
||||
|
||||
private pushTodoCard(column: 'backlog' | 'doing' | 'done', title: string) {
|
||||
|
||||
@ -193,6 +193,19 @@
|
||||
max-height: calc(100% - 36px);
|
||||
}
|
||||
|
||||
/* 隐藏窗口内所有滚动条(横向与纵向),仍保留滚动能力 */
|
||||
.virtual-monitor-surface .window,
|
||||
.virtual-monitor-surface .window * {
|
||||
scrollbar-width: none;
|
||||
-ms-overflow-style: none;
|
||||
}
|
||||
.virtual-monitor-surface .window::-webkit-scrollbar,
|
||||
.virtual-monitor-surface .window *::-webkit-scrollbar {
|
||||
display: none;
|
||||
width: 0;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
.virtual-monitor-surface .monitor-screen.manual-interactive .window-header {
|
||||
cursor: grab;
|
||||
user-select: none;
|
||||
@ -1056,28 +1069,54 @@
|
||||
}
|
||||
|
||||
.virtual-monitor-surface .memory-window {
|
||||
background: #fff9f2;
|
||||
border: 1px solid rgba(189, 93, 58, 0.3);
|
||||
background: rgba(244, 246, 251, 0.98);
|
||||
border: 1px solid rgba(15, 23, 42, 0.12);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.virtual-monitor-surface .memory-body {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.virtual-monitor-surface .memory-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
flex: 1 1 auto;
|
||||
min-height: 0;
|
||||
height: auto;
|
||||
max-height: none;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.virtual-monitor-surface .memory-item {
|
||||
border-radius: 14px;
|
||||
padding: 12px 14px;
|
||||
background: #fff;
|
||||
border: 1px solid rgba(189, 93, 58, 0.2);
|
||||
box-shadow: 0 8px 20px rgba(83, 41, 27, 0.12);
|
||||
border: 1px solid rgba(15, 23, 42, 0.08);
|
||||
box-shadow: 0 8px 20px rgba(15, 23, 42, 0.08);
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
gap: 10px;
|
||||
align-items: flex-start;
|
||||
flex: 0 0 auto;
|
||||
min-height: 44px;
|
||||
opacity: 0;
|
||||
transform: translateY(8px);
|
||||
transition: all 0.3s ease;
|
||||
transition: all 0.32s ease;
|
||||
}
|
||||
.virtual-monitor-surface .memory-text {
|
||||
line-height: 1.5;
|
||||
word-break: break-word;
|
||||
white-space: pre-wrap;
|
||||
flex: 1 1 auto;
|
||||
display: block;
|
||||
width: 100%;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.virtual-monitor-surface .memory-item.visible {
|
||||
@ -1085,14 +1124,19 @@
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.virtual-monitor-surface .memory-tag {
|
||||
font-size: 12px;
|
||||
letter-spacing: 0.1em;
|
||||
color: #a35a2d;
|
||||
min-width: 70px;
|
||||
text-transform: uppercase;
|
||||
.virtual-monitor-surface .memory-item.editing {
|
||||
box-shadow: 0 0 0 2px rgba(64, 99, 178, 0.25), 0 10px 26px rgba(15, 23, 42, 0.18);
|
||||
transform: translateY(-1px) scale(1.01);
|
||||
}
|
||||
|
||||
.virtual-monitor-surface .memory-item.swipe-out {
|
||||
opacity: 0;
|
||||
transform: translateX(140%);
|
||||
transition: transform 0.24s ease, opacity 0.22s ease;
|
||||
}
|
||||
|
||||
/* memory-tag removed to simplify UI */
|
||||
|
||||
.virtual-monitor-surface .todo-columns {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
|
||||
@ -146,7 +146,9 @@ connection_users: Dict[str, str] = {}
|
||||
stop_flags: Dict[str, Dict[str, Any]] = {}
|
||||
|
||||
MONITOR_FILE_TOOLS = {'append_to_file', 'modify_file', 'write_file_diff'}
|
||||
MONITOR_MEMORY_TOOLS = {'update_memory'}
|
||||
MONITOR_SNAPSHOT_CHAR_LIMIT = 60000
|
||||
MONITOR_MEMORY_ENTRY_LIMIT = 256
|
||||
RATE_LIMIT_BUCKETS: Dict[str, deque] = defaultdict(deque)
|
||||
FAILURE_TRACKERS: Dict[str, Dict[str, float]] = {}
|
||||
pending_socket_tokens: Dict[str, Dict[str, Any]] = {}
|
||||
@ -1617,6 +1619,21 @@ def gui_text_entry(terminal: WebTerminal, workspace: UserWorkspace, username: st
|
||||
return jsonify({"success": False, "error": str(exc)}), 400
|
||||
|
||||
|
||||
@app.route('/api/memory', methods=['GET'])
|
||||
@api_login_required
|
||||
@with_terminal
|
||||
def api_memory_entries(terminal: WebTerminal, workspace: UserWorkspace, username: str):
|
||||
"""返回主/任务记忆条目列表,供虚拟显示器加载"""
|
||||
memory_type = request.args.get('type', 'main')
|
||||
if memory_type not in ('main', 'task'):
|
||||
return jsonify({"success": False, "error": "type 必须是 main 或 task"}), 400
|
||||
try:
|
||||
entries = terminal.memory_manager._read_entries(memory_type) # type: ignore
|
||||
return jsonify({"success": True, "type": memory_type, "entries": entries})
|
||||
except Exception as exc:
|
||||
return jsonify({"success": False, "error": str(exc)}), 500
|
||||
|
||||
|
||||
@app.route('/api/gui/monitor_snapshot', methods=['GET'])
|
||||
@api_login_required
|
||||
def get_monitor_snapshot_api():
|
||||
@ -2791,6 +2808,12 @@ async def handle_task_with_sender(terminal: WebTerminal, message, sender, client
|
||||
return trimmed
|
||||
return None
|
||||
|
||||
|
||||
def resolve_monitor_memory(entries: Any) -> Optional[List[str]]:
|
||||
if isinstance(entries, list):
|
||||
return [str(item) for item in entries][:MONITOR_MEMORY_ENTRY_LIMIT]
|
||||
return None
|
||||
|
||||
def capture_monitor_snapshot(path: Optional[str]) -> Optional[Dict[str, Any]]:
|
||||
if not path:
|
||||
return None
|
||||
@ -4274,11 +4297,25 @@ async def handle_task_with_sender(terminal: WebTerminal, message, sender, client
|
||||
tool_display_id = f"tool_{iteration}_{function_name}_{time.time()}"
|
||||
monitor_snapshot = None
|
||||
snapshot_path = None
|
||||
memory_snapshot_type = None
|
||||
if function_name in MONITOR_FILE_TOOLS:
|
||||
snapshot_path = resolve_monitor_path(arguments)
|
||||
monitor_snapshot = capture_monitor_snapshot(snapshot_path)
|
||||
if monitor_snapshot:
|
||||
cache_monitor_snapshot(tool_display_id, 'before', monitor_snapshot)
|
||||
elif function_name in MONITOR_MEMORY_TOOLS:
|
||||
memory_snapshot_type = (arguments.get('memory_type') or 'main').lower()
|
||||
before_entries = None
|
||||
try:
|
||||
before_entries = resolve_monitor_memory(web_terminal.memory_manager._read_entries(memory_snapshot_type))
|
||||
except Exception as exc:
|
||||
debug_log(f"[MonitorSnapshot] 读取记忆失败: {memory_snapshot_type} ({exc})")
|
||||
if before_entries is not None:
|
||||
monitor_snapshot = {
|
||||
'memory_type': memory_snapshot_type,
|
||||
'entries': before_entries
|
||||
}
|
||||
cache_monitor_snapshot(tool_display_id, 'before', monitor_snapshot)
|
||||
|
||||
sender('tool_start', {
|
||||
'id': tool_display_id,
|
||||
@ -4344,6 +4381,23 @@ async def handle_task_with_sender(terminal: WebTerminal, message, sender, client
|
||||
if not result_path:
|
||||
result_path = resolve_monitor_path(arguments, snapshot_path) or snapshot_path
|
||||
monitor_snapshot_after = capture_monitor_snapshot(result_path)
|
||||
elif function_name in MONITOR_MEMORY_TOOLS:
|
||||
memory_after_type = str(
|
||||
arguments.get('memory_type')
|
||||
or (isinstance(result_data, dict) and result_data.get('memory_type'))
|
||||
or memory_snapshot_type
|
||||
or 'main'
|
||||
).lower()
|
||||
after_entries = None
|
||||
try:
|
||||
after_entries = resolve_monitor_memory(web_terminal.memory_manager._read_entries(memory_after_type))
|
||||
except Exception as exc:
|
||||
debug_log(f"[MonitorSnapshot] 读取记忆失败(after): {memory_after_type} ({exc})")
|
||||
if after_entries is not None:
|
||||
monitor_snapshot_after = {
|
||||
'memory_type': memory_after_type,
|
||||
'entries': after_entries
|
||||
}
|
||||
|
||||
update_payload = {
|
||||
'id': tool_display_id,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user