feat(agent-runner): add event-first context facts and pull APIs

Add EventLog and Transcript persistence entities for storing auditable
event facts and conversation history projection. Implement event-first
AgentRunContext builder that produces Protocol v1 compliant context
payloads with required fields: event, delivery, context (ContextAccess).

Key changes:
- EventLog ORM: auditable event records with indexes
- Transcript ORM: conversation history projection with composite indexes
- AgentRunContextBuilder: Protocol v1 payload with delivery, context, bootstrap
- EventLogStore/TranscriptStore: async stores for fact sources
- Host action handlers: HISTORY_PAGE, HISTORY_SEARCH, EVENT_GET, EVENT_PAGE
- Context validation: build_context output validates via SDK AgentRunContext
- Alembic migration for event_log and transcript tables
- Alembic env.py imports all ORM models for autogenerate discovery

Legacy compatibility: max-round messages go into bootstrap.messages and
compatibility.legacy_messages, not top-level messages field.
This commit is contained in:
huanghuoguoguo
2026-05-23 16:07:46 +08:00
parent 8063303cfa
commit 8db23bf950
18 changed files with 3705 additions and 60 deletions

View File

@@ -420,9 +420,12 @@ class TestBuildParamsInContext:
context = await builder.build_context(query, descriptor, resources)
assert 'params' in context
assert context['params']['public_param'] == 'value'
assert '_private' not in context['params']
# Protocol v1: params is in compatibility.extra
assert 'compatibility' in context
assert 'extra' in context['compatibility']
assert 'params' in context['compatibility']['extra']
assert context['compatibility']['extra']['params']['public_param'] == 'value'
assert '_private' not in context['compatibility']['extra']['params']
@pytest.mark.asyncio
async def test_params_and_state_both_present(self):
@@ -454,10 +457,12 @@ class TestBuildParamsInContext:
context = await builder.build_context(query, descriptor, resources)
# params should have public vars
assert 'params' in context
assert context['params']['workflow_input'] == 'user_question'
assert context['params']['sender_name'] == 'John'
# Protocol v1: params is in compatibility.extra
assert 'compatibility' in context
assert 'extra' in context['compatibility']
assert 'params' in context['compatibility']['extra']
assert context['compatibility']['extra']['params']['workflow_input'] == 'user_question'
assert context['compatibility']['extra']['params']['sender_name'] == 'John'
# state should have seeded conversation_id
assert 'state' in context
@@ -490,6 +495,10 @@ class TestBuildParamsInContext:
context = await builder.build_context(query, descriptor, resources)
assert context['prompt'][0]['content'] == 'Effective prompt'
# Protocol v1: prompt is in compatibility.extra
assert 'compatibility' in context
assert 'extra' in context['compatibility']
assert 'prompt' in context['compatibility']['extra']
assert context['compatibility']['extra']['prompt'][0]['content'] == 'Effective prompt'
assert context['runtime']['metadata']['streaming_supported'] is True
assert context['runtime']['metadata']['remove_think'] is True