Refactor session runtime persistence
This commit is contained in:
@@ -1025,8 +1025,13 @@ class AgentRuntimeTests(unittest.TestCase):
|
||||
message for message in result.transcript
|
||||
if message.get('metadata', {}).get('kind') == 'compact_boundary'
|
||||
]
|
||||
self.assertEqual(len(transcript_compact_messages), 1)
|
||||
compact_metadata = transcript_compact_messages[0].get('metadata', {})
|
||||
self.assertEqual(len(transcript_compact_messages), 0)
|
||||
model_compact_messages = [
|
||||
message for message in (agent.last_session.messages if agent.last_session else [])
|
||||
if message.metadata.get('kind') == 'compact_boundary'
|
||||
]
|
||||
self.assertEqual(len(model_compact_messages), 1)
|
||||
compact_metadata = model_compact_messages[0].metadata
|
||||
self.assertEqual(compact_metadata.get('compaction_depth'), 1)
|
||||
self.assertEqual(compact_metadata.get('nested_compaction_count'), 0)
|
||||
self.assertIn('preserved_tail_ids', compact_metadata)
|
||||
|
||||
@@ -255,6 +255,10 @@ class TestCompactConversation(unittest.TestCase):
|
||||
self.assertNotIn('<analysis>', result.summary_text)
|
||||
# Summary should contain the actual summary content
|
||||
self.assertIn('User wanted to test compaction', result.summary_text)
|
||||
# UI display transcript must remain append-only and not be replaced by
|
||||
# compact_summary / compact_boundary model-context messages.
|
||||
display_contents = [m.content for m in agent.last_session.display_messages]
|
||||
self.assertEqual(display_contents, [m.content for m in msgs])
|
||||
|
||||
def test_api_error_returns_compaction_error(self) -> None:
|
||||
with tempfile.TemporaryDirectory() as tmp_dir:
|
||||
|
||||
@@ -483,6 +483,21 @@ class GuiServerTests(unittest.TestCase):
|
||||
},
|
||||
{'role': 'user', 'content': 'continue'},
|
||||
),
|
||||
display_messages=(
|
||||
{'role': 'user', 'content': 'hello'},
|
||||
{
|
||||
'role': 'assistant',
|
||||
'content': '上一次任务已中断,后台没有正在执行的进程。',
|
||||
'state': 'final',
|
||||
'stop_reason': 'interrupted',
|
||||
'metadata': {'kind': 'run_status', 'status': 'interrupted'},
|
||||
},
|
||||
{
|
||||
'role': 'user',
|
||||
'content': 'pending',
|
||||
'metadata': {'status': 'running', 'placeholder': True},
|
||||
},
|
||||
),
|
||||
turns=1,
|
||||
tool_calls=0,
|
||||
usage={},
|
||||
@@ -498,6 +513,10 @@ class GuiServerTests(unittest.TestCase):
|
||||
[message['content'] for message in sanitized.messages],
|
||||
['hello', 'continue'],
|
||||
)
|
||||
self.assertEqual(
|
||||
[message['content'] for message in sanitized.display_messages],
|
||||
['hello'],
|
||||
)
|
||||
|
||||
def test_runtime_event_stage_names_long_running_work(self) -> None:
|
||||
self.assertEqual(
|
||||
|
||||
@@ -80,6 +80,7 @@ class TestStoredAgentSessionRoundTrip(unittest.TestCase):
|
||||
'user_context': {'lang': 'en'},
|
||||
'system_context': {'os': 'linux'},
|
||||
'messages': ({'role': 'user', 'content': 'hi'},),
|
||||
'display_messages': ({'role': 'user', 'content': 'visible hi'},),
|
||||
'turns': 3,
|
||||
'tool_calls': 7,
|
||||
'usage': {'input_tokens': 500, 'output_tokens': 300},
|
||||
@@ -106,6 +107,7 @@ class TestStoredAgentSessionRoundTrip(unittest.TestCase):
|
||||
self.assertEqual(loaded.user_context, session.user_context)
|
||||
self.assertEqual(loaded.system_context, session.system_context)
|
||||
self.assertEqual(loaded.messages, session.messages)
|
||||
self.assertEqual(loaded.display_messages, session.display_messages)
|
||||
self.assertEqual(loaded.turns, session.turns)
|
||||
self.assertEqual(loaded.tool_calls, session.tool_calls)
|
||||
self.assertEqual(loaded.usage, session.usage)
|
||||
@@ -157,6 +159,30 @@ class TestStoredAgentSessionRoundTrip(unittest.TestCase):
|
||||
self.assertEqual(len(loaded.messages), 2)
|
||||
self.assertEqual(loaded.messages[0]['role'], 'user')
|
||||
self.assertEqual(loaded.messages[1]['role'], 'assistant')
|
||||
self.assertEqual(loaded.display_messages, loaded.messages)
|
||||
|
||||
def test_load_agent_session_falls_back_to_messages_for_display(self) -> None:
|
||||
with tempfile.TemporaryDirectory() as td:
|
||||
directory = Path(td)
|
||||
path = directory / 'legacy.json'
|
||||
data = {
|
||||
'session_id': 'legacy',
|
||||
'model_config': {},
|
||||
'runtime_config': {'cwd': '/'},
|
||||
'system_prompt_parts': [],
|
||||
'user_context': {},
|
||||
'system_context': {},
|
||||
'messages': [{'role': 'user', 'content': 'legacy hi'}],
|
||||
'turns': 0,
|
||||
'tool_calls': 0,
|
||||
'usage': {},
|
||||
'total_cost_usd': 0.0,
|
||||
'file_history': [],
|
||||
}
|
||||
path.write_text(json.dumps(data))
|
||||
loaded = load_agent_session('legacy', directory=directory)
|
||||
|
||||
self.assertEqual(loaded.display_messages, loaded.messages)
|
||||
|
||||
def test_load_defaults_for_missing_optional_fields(self) -> None:
|
||||
"""Missing optional fields get sensible defaults."""
|
||||
|
||||
Reference in New Issue
Block a user