Refactor session runtime persistence

This commit is contained in:
wuyang6
2026-06-12 16:17:58 +08:00
parent 77d360c1e8
commit 953126e1d3
15 changed files with 717 additions and 253 deletions
+7 -2
View File
@@ -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)
+4
View File
@@ -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:
+19
View File
@@ -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(
+26
View File
@@ -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."""