fixes: MCP auto-continue lists tool names + history doesn't eat chats + WS strip replayed closes

(1) After MCPActivate, the continuation prompt now enumerates the
activated server's callable tool names so the next turn calls them
directly instead of burning a turn on tool-discovery guesses.
(2) Resume no longer deletes the session file from disk; previously
every click on a past chat permanently removed it from history.
(3) WS replay skips agent:closed events; replaying them on a fresh
client destructively deleted the session being opened.
This commit is contained in:
ciregenz
2026-05-20 02:10:12 -07:00
parent 6092fc0966
commit ac99b0090b
3 changed files with 60 additions and 3 deletions
+6 -1
View File
@@ -4504,7 +4504,12 @@ class AgentManager:
session.closed_at = None
self.sessions[session_id] = session
_delete_session_file(session_id)
# Do NOT delete the disk file here. The history list (get_history)
# reads from disk; deleting on resume meant every click on a past
# chat permanently removed it from history on the next restart.
# The disk copy stays as the durable record; subsequent turn
# completions and close_session calls overwrite it via
# _save_session, so memory and disk stay in sync.
await ws_manager.send_to_session(session_id, "agent:status", {
"session_id": session_id,
+25
View File
@@ -148,6 +148,7 @@ class ConnectionManager:
# reconcile_on_startup also marks waiting_approval sessions as
# stopped so there's nothing to answer anyway.
events = self._filter_stale_approvals(events)
events = self._strip_replayed_closes(events)
for s in events:
try:
await websocket.send_text(s)
@@ -177,6 +178,30 @@ class ConnectionManager:
"current_seq": newest if newest is not None else 0,
}
def _strip_replayed_closes(self, events: list[str]) -> list[str]:
"""Drop `agent:closed` events from a replay buffer.
agent:closed is a transition event ("session JUST closed") whose
frontend reducer (closeSessionFromWs) destructively deletes the
session from state.sessions. Replaying it on a fresh client (e.g.
a user who just clicked the closed chat in history) deletes the
session they're trying to open. The current closed state is
already conveyed by the REST hydrate (status=stopped, closed_at
set) and by the latest agent:status event in the replay, so
suppressing the transition replay is non-lossy.
"""
out: list[str] = []
for payload_str in events:
try:
parsed = json.loads(payload_str)
except (ValueError, TypeError):
out.append(payload_str)
continue
if parsed.get("event") == "agent:closed":
continue
out.append(payload_str)
return out
def _filter_stale_approvals(self, events: list[str]) -> list[str]:
"""Return events minus any `agent:approval_request` whose request_id
is no longer in pending_futures. JSON parse is per-event but replay
+29 -2
View File
@@ -667,11 +667,38 @@ async def mcp_meta(action: str, request: Request):
# Turns the typical 3-prompt flow ("check email" → MCPActivate
# → "do it") into a 1-prompt flow.
session.pending_continuation = True
# Enumerate the just-activated server's callable tool names so the
# continuation turn can call them directly. Without this the model
# often burns a turn on tool-discovery guesses (Bash "mcp list",
# Ls /toolbox, ToolSearch fallbacks) before landing on the right
# mcp__server__action name. Cap at 16 + clip descriptions so the
# prompt stays bounded for kitchen-sink servers (google-workspace
# exposes ~30 tools). Best-effort; any lookup failure silently
# falls back to the same prompt this code shipped with before.
tool_hint = ""
try:
for t in load_all_tools():
if _sanitize_server_name(t.name) != server_name:
continue
descs = (t.tool_permissions or {}).get("_tool_descriptions", {}) or {}
if not descs:
break
lines: list[str] = []
for sub_name, desc in list(descs.items())[:16]:
short = (desc or "").strip().split("\n", 1)[0][:120]
visible = f"mcp__{server_name}__{sub_name}"
lines.append(f"- `{visible}`: {short}" if short else f"- `{visible}`")
if lines:
more = "" if len(descs) <= 16 else f"\n(+ {len(descs) - 16} more; call ToolSearch with the server name for the rest)"
tool_hint = "\n\nCallable tools on this server:\n" + "\n".join(lines) + more
break
except Exception:
logger.exception("activate: failed to build tool hint for %s", server_name)
session.pending_continuation_prompt = (
"[mcp:auto-continue] The MCP server you requested has been "
f"activated (`{server_name}`). Continue with the user's original "
"request now using the newly-available tools do NOT ask "
"for confirmation."
"request now using the newly-available tools; do NOT ask "
"for confirmation." + tool_hint
)
return JSONResponse({"status": "activated", "server_name": server_name, "auto_continue": True})