refactor(acp): new/load/resume share _attach_session_mcp; replay guard folded into _session_response_fields

This commit is contained in:
Teknium
2026-09-02 19:32:09 -07:00
parent a26343bbcd
commit dde39d51cd
+26 -28
View File
@@ -566,21 +566,22 @@ class HermesACPAgent(SlashCommandsMixin, acp.Agent):
if not await self._send(state.session_id, update, fail_msg="Failed to replay ACP history for session %s"):
return
async def _replay_history_guarded(self, state: SessionState, verb: str) -> None:
"""Per ACP spec, load/resume must stream history via ``session/update`` BEFORE
responding (Codex/Claude Code/OpenCode/Zed rely on this; deferring via ``call_soon``
broke them). Best-effort: a corrupt message must not turn the load into an error."""
try:
await self._replay_session_history(state)
except Exception:
logger.warning(
f"ACP history replay raised during session/{verb} for %s — "
f"{verb} will still succeed, partial transcript may be missing", state.session_id, exc_info=True,
)
async def _session_response_fields(self, state: SessionState, replay_verb: str | None = None) -> dict[str, Any]:
"""``models``/``modes``/``field_meta`` for session responses, after an optional history replay;
schedules command advertisement + usage refresh.
def _session_response_fields(self, state: SessionState) -> dict[str, Any]:
"""``models``/``modes``/``field_meta`` for session responses; schedules command
advertisement + usage refresh."""
Per ACP spec, load/resume must stream history via ``session/update`` BEFORE responding
(Codex/Claude Code/OpenCode/Zed rely on this; deferring via ``call_soon`` broke them).
Best-effort: a corrupt message must not turn the load into an error."""
if replay_verb:
try:
await self._replay_session_history(state)
except Exception:
logger.warning(
f"ACP history replay raised during session/{replay_verb} for %s — "
f"{replay_verb} will still succeed, partial transcript may be missing",
state.session_id, exc_info=True,
)
self._schedule_available_commands_update(state.session_id)
self._schedule_soon(lambda: self._send_usage_update(state))
return {
@@ -589,12 +590,15 @@ class HermesACPAgent(SlashCommandsMixin, acp.Agent):
"field_meta": self._provenance_meta(state.session_id, getattr(state.agent, "session_id", state.session_id)),
}
async def new_session(self, cwd: str, mcp_servers: list | None = None, **kwargs: Any) -> NewSessionResponse:
state = self.session_manager.create_session(cwd=cwd)
async def _attach_session_mcp(self, state: SessionState, mcp_servers: list | None, log: str, *log_args) -> None:
await self._register_session_mcp_servers(state, mcp_servers)
self._schedule_mcp_late_refresh(state)
logger.info("New session %s (cwd=%s)", state.session_id, cwd)
return NewSessionResponse(session_id=state.session_id, **self._session_response_fields(state))
logger.info(log, *log_args)
async def new_session(self, cwd: str, mcp_servers: list | None = None, **kwargs: Any) -> NewSessionResponse:
state = self.session_manager.create_session(cwd=cwd)
await self._attach_session_mcp(state, mcp_servers, "New session %s (cwd=%s)", state.session_id, cwd)
return NewSessionResponse(session_id=state.session_id, **await self._session_response_fields(state))
async def load_session(
self, cwd: str, session_id: str, mcp_servers: list | None = None, **kwargs: Any
@@ -603,11 +607,8 @@ class HermesACPAgent(SlashCommandsMixin, acp.Agent):
if state is None:
logger.warning("load_session: session %s not found", session_id)
return None
await self._register_session_mcp_servers(state, mcp_servers)
self._schedule_mcp_late_refresh(state)
logger.info("Loaded session %s", session_id)
await self._replay_history_guarded(state, "load")
return LoadSessionResponse(**self._session_response_fields(state))
await self._attach_session_mcp(state, mcp_servers, "Loaded session %s", session_id)
return LoadSessionResponse(**await self._session_response_fields(state, "load"))
async def resume_session(
self, cwd: str, session_id: str, mcp_servers: list | None = None, **kwargs: Any
@@ -616,11 +617,8 @@ class HermesACPAgent(SlashCommandsMixin, acp.Agent):
if state is None:
logger.warning("resume_session: session %s not found, creating new", session_id)
state = self.session_manager.create_session(cwd=cwd)
await self._register_session_mcp_servers(state, mcp_servers)
self._schedule_mcp_late_refresh(state)
logger.info("Resumed session %s", state.session_id)
await self._replay_history_guarded(state, "resume")
return ResumeSessionResponse(**self._session_response_fields(state))
await self._attach_session_mcp(state, mcp_servers, "Resumed session %s", state.session_id)
return ResumeSessionResponse(**await self._session_response_fields(state, "resume"))
async def cancel(self, session_id: str, **kwargs: Any) -> None:
state = self.session_manager.get_session(session_id)