mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-11-06 05:17:15 +00:00
fix(acp): defer attach notices until options are ready
Finish the awaited service-tier read before scheduling post-attach command
notices, so an editor has its session reply before the command menu. Read
the latest edit policy after that await for both reply representations.
The command-order regression fails on 42500a10d9. A second regression
catches mode changes during discovery. Both pass after the correction.
This commit is contained in:
1 parent
ea9cabf491
commit
d761fb9d79
2 files changed
+66
-6
No files matched your search
@@ -300,22 +300,26 @@ class GatewayACPAgent(GatewayReplyMixin, acp.Agent):
|
||||
"""Main's new/load/resume/fork reply fields (model picker, modes, provenance ``_meta``); the
|
||||
command list and context indicator follow once the reply is queued, as main scheduled them."""
|
||||
from acp_adapter.gateway_session_meta import model_state, provenance_meta, session_config_options, session_modes
|
||||
from acp_adapter.modes import MODE_TO_EDIT_APPROVAL_POLICY
|
||||
from acp_adapter.modes import EDIT_APPROVAL_POLICY_CONFIG_ID, MODE_TO_EDIT_APPROVAL_POLICY
|
||||
title = info.get("title")
|
||||
if isinstance(title, str) and title.strip():
|
||||
self._titles[session_id] = title
|
||||
models = await asyncio.to_thread(model_state, info)
|
||||
meta = await asyncio.to_thread(provenance_meta, self._home / "state.db", editor_id, session_id)
|
||||
mode = self._modes.setdefault(session_id, session_modes(info).current_mode_id)
|
||||
options = await session_config_options(self._gateway, session_id, models, info, mode)
|
||||
# ``_adopt`` seeded the mode from the snapshot: a change the pump projected since is what the reply shows.
|
||||
policy = MODE_TO_EDIT_APPROVAL_POLICY[self._modes[session_id]]
|
||||
modes = session_modes({"edit_approval_policy": policy})
|
||||
for option in options:
|
||||
if option.id == EDIT_APPROVAL_POLICY_CONFIG_ID:
|
||||
option.current_value = policy
|
||||
if self._conn:
|
||||
task = asyncio.get_running_loop().create_task(self._after_attach(session_id, editor_id))
|
||||
self._background.add(task)
|
||||
task.add_done_callback(self._background.discard)
|
||||
# ``_adopt`` seeded the mode from the snapshot: a change the pump projected since is what the reply shows.
|
||||
modes = session_modes({"edit_approval_policy": MODE_TO_EDIT_APPROVAL_POLICY[self._modes.setdefault(
|
||||
session_id, session_modes(info).current_mode_id)]})
|
||||
return {"models": models, "modes": modes, "field_meta": meta,
|
||||
"config_options": await session_config_options(
|
||||
self._gateway, session_id, models, info, modes.current_mode_id)}
|
||||
"config_options": options}
|
||||
|
||||
async def _after_attach(self, session_id, editor_id):
|
||||
from acp_adapter.gateway_session_meta import available_commands_update
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
"""Attach command notices follow the reply even when speed discovery yields."""
|
||||
import asyncio
|
||||
from unittest.mock import AsyncMock
|
||||
|
||||
import pytest
|
||||
|
||||
from acp_adapter.gateway_server import GatewayACPAgent
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_speed_discovery_does_not_publish_commands_before_the_attach_reply(monkeypatch):
|
||||
monkeypatch.setattr('acp_adapter.gateway_session_meta.model_state', lambda info: None)
|
||||
monkeypatch.setattr('acp_adapter.gateway_session_meta.provenance_meta', lambda *args: None)
|
||||
agent = GatewayACPAgent()
|
||||
reply_returned = False
|
||||
command_order = []
|
||||
|
||||
async def session_update(*, session_id, update):
|
||||
if update.session_update == 'available_commands_update':
|
||||
command_order.append(reply_returned)
|
||||
|
||||
async def rpc(method, **params):
|
||||
if method == 'config.get':
|
||||
await asyncio.gather(*tuple(agent._background))
|
||||
return {'value': 'normal', 'options': ['normal', 'fast']}
|
||||
return {}
|
||||
|
||||
owner = AsyncMock()
|
||||
owner.rpc.side_effect = rpc
|
||||
agent._gateway = owner
|
||||
monkeypatch.setattr(agent, '_client', AsyncMock(return_value=owner))
|
||||
agent._conn = AsyncMock()
|
||||
agent._conn.session_update.side_effect = session_update
|
||||
fields = await agent._session_fields('session', {'model': 'gpt-6.1-sol'}, 'session')
|
||||
reply_returned = True
|
||||
await asyncio.gather(*tuple(agent._background))
|
||||
assert any(option.id == 'service_tier' for option in fields['config_options'])
|
||||
assert command_order == [True]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_mode_changed_during_speed_discovery_is_used_by_the_attach_reply(monkeypatch):
|
||||
monkeypatch.setattr('acp_adapter.gateway_session_meta.model_state', lambda info: None)
|
||||
monkeypatch.setattr('acp_adapter.gateway_session_meta.provenance_meta', lambda *args: None)
|
||||
agent = GatewayACPAgent()
|
||||
|
||||
async def rpc(method, **params):
|
||||
await agent._project_mode('session', 'session', {'edit_approval_policy': 'session'})
|
||||
return {'value': 'normal', 'options': ['normal', 'fast']}
|
||||
|
||||
owner = AsyncMock()
|
||||
owner.rpc.side_effect = rpc
|
||||
agent._gateway = owner
|
||||
fields = await agent._session_fields('session', {'model': 'gpt-6.1-sol'}, 'session')
|
||||
assert fields['modes'].current_mode_id == 'dont_ask'
|
||||
assert next(option for option in fields['config_options'] if option.category == 'mode').current_value == 'session'
|
||||
Reference in new issue
Block a user