diff --git a/backend/api/server.py b/backend/api/server.py index 97d2ae2..3cfdd80 100644 --- a/backend/api/server.py +++ b/backend/api/server.py @@ -71,6 +71,7 @@ from src.run_state_store import ACTIVE_RUN_STATUSES, RunStateStore from src.session_store import ( DEFAULT_AGENT_SESSION_DIR, StoredAgentSession, + append_agent_display_message, agent_session_delta, cancel_session_input_queue, consume_session_guidance, @@ -1834,6 +1835,12 @@ def create_app(state: AgentState) -> FastAPI: content=content, metadata=payload.metadata, ) + if item.get('kind') == 'guidance': + _append_guidance_display_message( + safe_id, + directory=directory, + item=item, + ) return {'session_id': safe_id, 'item': item} @app.patch('/api/sessions/{session_id}/input-queue/{item_id}') @@ -1857,6 +1864,12 @@ def create_app(state: AgentState) -> FastAPI: ) if item is None: raise HTTPException(status_code=404, detail='Queue item not found') + if item.get('kind') == 'guidance' and item.get('status') == 'pending': + _append_guidance_display_message( + safe_id, + directory=directory, + item=item, + ) return {'session_id': safe_id, 'item': item} @app.delete('/api/sessions/{session_id}/input-queue/{item_id}') @@ -3050,6 +3063,40 @@ def _normalize_transcript_entry(entry: dict[str, Any]) -> dict[str, Any]: return out +def _append_guidance_display_message( + session_id: str, + *, + directory: Path, + item: dict[str, Any], +) -> None: + content = str(item.get('content') or '').strip() + item_id = item.get('id') + if ( + not content + or not isinstance(item_id, int) + or isinstance(item_id, bool) + ): + return + message_id = f'user_guidance_{item_id}' + append_agent_display_message( + session_id, + { + 'role': 'user', + 'content': content, + 'state': 'final', + 'message_id': message_id, + 'metadata': { + 'kind': 'runtime_guidance_display', + 'queue_item_id': item_id, + 'lineage_id': message_id, + 'run_id': str(item.get('run_id') or ''), + 'created_at': float(item.get('created_at') or time.time()), + }, + }, + directory=directory, + ) + + def _truncate_api_text(text: str, limit: int) -> str: if len(text) <= limit: return text diff --git a/frontend/app/components/assistant-ui/thread.tsx b/frontend/app/components/assistant-ui/thread.tsx index 2798255..f0beb80 100644 --- a/frontend/app/components/assistant-ui/thread.tsx +++ b/frontend/app/components/assistant-ui/thread.tsx @@ -985,7 +985,10 @@ const ComposerPendingInputQueue: FC<{ sessionId: string | null }> = ({ status: "pending", }) .catch(() => undefined) - .finally(dispatchInputQueueChanged); + .finally(() => { + dispatchInputQueueChanged(); + window.dispatchEvent(new Event("claw-sessions-changed")); + }); }} > 引导 diff --git a/src/session_store.py b/src/session_store.py index d553cba..a353168 100644 --- a/src/session_store.py +++ b/src/session_store.py @@ -213,6 +213,79 @@ def agent_session_delta( } +def append_agent_display_message( + session_id: str, + message: JSONDict, + directory: Path | None = None, +) -> dict[str, Any] | None: + target_dir = directory or DEFAULT_AGENT_SESSION_DIR + if not isinstance(message, dict): + return None + message_key = _display_message_key(message) + message_json = json.dumps(message, ensure_ascii=False, sort_keys=True) + now = time.time() + with _connect_agent_session_db(target_dir) as conn: + existing = conn.execute( + """ + select seq, message_json + from agent_display_messages + where session_id = ? and message_key = ? + order by seq asc + limit 1 + """, + (session_id, message_key), + ).fetchone() + if existing is not None: + if existing['message_json'] != message_json: + conn.execute( + """ + update agent_display_messages + set role = ?, + updated_at = ?, + message_json = ? + where session_id = ? and seq = ? + """, + ( + str(message.get('role') or ''), + now, + message_json, + session_id, + int(existing['seq']), + ), + ) + return { + 'seq': int(existing['seq']), + 'updated_at': now, + 'message': message, + } + latest = conn.execute( + """ + select max(seq) as latest_seq + from agent_display_messages + where session_id = ? + """, + (session_id,), + ).fetchone() + seq = int(latest['latest_seq'] or 0) + 1 if latest is not None else 1 + conn.execute( + """ + insert into agent_display_messages ( + session_id, seq, message_key, role, updated_at, message_json + ) + values (?, ?, ?, ?, ?, ?) + """, + ( + session_id, + seq, + message_key, + str(message.get('role') or ''), + now, + message_json, + ), + ) + return {'seq': seq, 'updated_at': now, 'message': message} + + def enqueue_session_input( session_id: str, *,